当前位置导航:炫浪网>>网络学院>>操作系统>>Linux教程

Linux中文化之在XFree86窗口系统中实现对GB18030的支持(二)


  苏哲 ([email protected])
  Turbo Linux公司软件工程师
  本文是《在 XFree86 窗口系统中实现对 GB18030 的支持》的第二篇,将具体介绍如何在XFree86中实现对 GB18030的支持。
  1. 在 libX11 中实现 GB18030 编码转换模块
  前面已经介绍了在 XFree86 中支持一种文字编码的关键是在 libX11 中实现支持这种编码的一系列转换模块。 但是由于 GB18030 是单双四字节混和编码, 所以和其它常用的单双字节编码相比, 处理起来比较麻烦。 下面就简要介绍一下在 libX11 中实现 GB18030 编码转换函数的原理。 大家可以参考 XFree86 的源码和相关文档, 以便更好的理解以下内容。
  1.1 如何将 GB18030 编码成 Compound Text
  看过 Compound Text 文档, 大家就会知道它的最初用途是用来承载单字节或双字节编码的, 并不适合承载编码 GB18030 这样的多字节可变长度编码。其实 Compound Text 所能够承载的编码并不是通常意义的文本编码(encoding),而是字符集编码(CharSet encoding)。而 XFree86 系统最多仅支持双字节宽度的字符集编码。
  要想用 Compound Text 承载 GB18030 编码的文本, 就必须先把 GB18030 转换成 Compound Text 所支持的编码。 目前可行的有以下两种方案:
  方案一、将 GB18030 拆分成两个双字节编码首先需要注意的是 GB18030 编码中 0x0~0x7F 范围的单字节部分用 Compound Text 编码后就变成了 ISO8859-1:GL (GL 表示左半部分编码, 即 ISO8859-1 的七位码部分), 所以在拆分 GB18030 编码的时候, 这部分是不计入内的。
  拆分 GB18030 的最直接的办法就是把其双字节部分作为一个独立的编码, 然后把四字节部分再编码成双字节, 成为另一个独立的编码。 这样我们就可以将 GB18030 文本分解成一个单字节部分, 和两个双字节部分编码成 Compound Text 了。 GB18030 的双字节部分其实基本就是原来的 GBK 双字节编码部分, 它们之间仅有几十个码位的区别。 也就是说我们可以象处理 GBK 编码那样处理 GB18030 的双字节部分。 下面的任务就是处理四字节部分了。
  将 GB18030 四字节部分编码成双字节最简单的办法就是把这部分编码从第一个码开始按顺序重新编码, 重新编码后 GB18030 的第一个四字节码 0x81308130 就变成了 0。 依次类推, 我们可以将 GB18030 四字节编码中与 Unicode 3.0 (即 ISO10646 第一平面) 相对应的部分编码成双字节。 但由于这种编码方式最多只能有 65536 个码位, 所以我们不可能将 GB18030 中与 Unicode 3.1 对应的所有码位都编成双字节码。 这也是这种编码方案最大的不足。 另外还有一种办法就是直接将 GB18030 四字节编码转换成对应的 UCS2 编码, 同样可以实现以上目的。
  在实际实现时, 我们并没有使用将 GB18030 拆分成两个双字节编码的方案。 因为这种方案实现起来过于复杂, 而且还具有无法向 Unicode 3.1 标准过渡等问题。 实际使用的是下面一种方案。
  方案二、将 GB18030 编码成 UTF-8
  前面已经介绍过,为了使 XFree86 平稳的从 Compound Text 过渡到 UTF-8,已经在标准的 Compound Text 中加入了一个特殊的模式用来编码 UTF-8文本。在 XFree86 中 Compound Text 编码转换函数会对这种编码模式做特别处理,以便能够正确转换编码在 Compound Text 中的 UTF-8 文本。详细细节请参见 XFree86 的源码(xc/lib/X11/lcCT.c)。
  另外前面也介绍过,GB18030 在字汇上和 Unicode 3.0 标准是一一对应的,在码位上也给 Unicode 3.1 标准预留了足够的空间。也就是说,GB18030 编码和 Unicode 3.1 编码是一一对应关系,它们之间可以做双向转换而不损失任何信息。所以我们完全可以将 GB18030 编码的文本先转换成 UTF-8 编码,然后再转换成 Compound Text,而不丢失任何信息,反之亦然。
  由于 libX11 中已经提供了通用的函数完成 CharSet encoding 与 Compound Text 之间的转换,并且支持 UTF-8 编码。所以我们只需要提供 GB18030 <-> UTF-8 的转换函数,就可以利用 libX11 的间接编码转换功能实现 GB18030 <-> Compound Text 的相互转换。这一过程对于应用程序来说是完全透明的,只要应用程序使用 libX11 所提供的标准的 Compound Text 处理函数,就可以正确无误的处理 GB18030 编码。目前常用的应用程序开发库,如 gtk+、QT 等都是这样做的。但不幸的是有少数应用软件是自己处理 Compound Text 的,如 emacs 和 xemacs。所以这种方案不能应用于这类软件。另外,gtk+ 从 1.2.9 开始便引入了一个错误,它在处理 Compound Text 文本时画蛇添足的做了一下过滤,把 0x80~0xFF 之间的一些控制字符过滤掉了,就破坏了编码成 UTF-8 的 Compound Text 文本。在最新发布的 TurboLinux 7.0 中已经修正了这个错误。
  至此,将 GB18030 编码成 Compound Text 的问题已经解决了。接下来就要解决 GB18030 与其它编码之间的转换问题,以及显示的问题了。
  1.2 GB18030 与其它编码之间的相互转换以上介绍了如何将 GB18030 编码成 Compound Text 的问题,解决了这个问题后我们就可以在 GB18030 Locale 下运行的两个应用程序之间利用 Compound Text 交换数据了。但这还远远不够。
  目前最常用的中文编码是 GB2312 和 GBK,台湾和香港地区常用的则是 BIG5 和 BIG5-HKSCS。GB18030 作为最新的汉字编码标准其实已经涵盖了前面这几种编码标准的绝大部分字汇,是名副其实的超集。因此,在使用传统中文编码的应用程序和使用 GB18030 的应用程序之间交换数据,是一个非常有用的功能。至少需要实现从传统编码程序到 GB18030 程序的单向数据传输。因此我们必须在 libX11 中实现把 GB2312、GBK、BIG5 等编码转换成 GB18030 编码的功能。更广泛的讲,GB18030 标准已经具备表示所有 Unicode 3.0 甚至 Unicode 3.1 字汇的能力,也就是说 GB18030 可以编码各国文字。所以将非中文编码转换成 GB18030 编码也是有意义的,尤其是日文和韩文。
  要想实现从各种传统编码向 GB18030 编码转换可以借助于 glibc 的 iconv 模块,但这种方法的最大缺陷就是效率太低。幸运的是,在 libX11 中已经包含了许多常用字符集编码和 UCS4 之间的转换函数(参见 xc/lib/X11/lcUTF8.c 和 xc/lib/X11/lcUniConv/*)。所以我们可以利用这些函数通过 UCS4 编码做中介,实现 GB18030 和这些传统编码之间的转换。
  1.3 GB18030 编码字符串的显示
  前面已经提到,XFree86 仅支持双字节的字符集编码,所以直接用 GB18030 编码做字符集编码进行字符串显示是不行的。因此我们需要将 GB18030 编码的字符串先转换成多段单字节或双字节编码的字符串,然后才能送去显示。这个问题的解决办法其实已经在 1.1 节中提到了。
  对于第一个方案,我们已经把 GB18030 编码的字符串分解为一个单字节编码部分和两个双字节编码部分,因此可以直接送去显示。单字节部分就是 ISO8859-1 编码,可以使用 ISO8859-1 编码的英文字库进行显示;双字节部分则需要中文字库的支持。在现有的应用软件中,mozilla 其实就是使用这种方法来显示 GB18030 文本的。mozilla 使用了 1.1 中所述的第一种方法编码 GB18030 文本,并将 GBK 兼容的双字节编码部分称做 gb18030.2000-0,将原四字节编码部分称做 gb18030.2000-1。因为 mozilla 不使用字符集来显示字符串,它使用自己的一套处理机制来完成类似于字符集的功能,因此只要 XFree86 支持这两种编码的字库,mozilla 就可以正确显示 GB18030 文本了。关于在 XFree86 中支持这两种字库的问题,我们将在后面介绍。
  对于第二个方案,就更简单了。我们只需要用一个 iso10646-1 (UCS2-BE) 编码的 GB18030 中文字库就可以完成字符串的显示工作。而且现在所有符合 GB18030 标准的 TrueType 字库都使用了 Unicode (UCS2-BE) 编码作为字库索引,XFree86 的 xtt/freetype 字库模块可以直接访问这种字库而不用做任何编码转换。但是由于中文 TrueType 字库包含的字模数目太大,在 XFree86 中用变宽模式使用这种字库,速度太慢。所以我们只能用固定宽度的字符元(Char Cell)模式来使用中文字库,由此导致的直接后果就是所有英文字符变成全宽的了,效果非常难看。
  解决这个问题的方法其实很简单,即使用字符集。将 GB18030 编码文本中 0x0~0x7F 的部分分离出来,使用半宽的英文字库进行显示。其余部分仍然使用中文字库进行显示。在实践中发现,由于 GB18030 还覆盖了 Unicode 中 0x80~0xFF 之间的编码,即 ISO8859-1 的右半部分。而这部分也是半宽的,所以同样不能使用中文字库进行显示。因此这部分也需要被分离出来使用英文字库进行显示。
  TrueType 字库在显示低点阵字符的时候效果比较差,目前又没有可用的 GB18030 点阵字库,所以在实际应用中我们还把 GB18030 中常用的 GB2312 部分编码分离了出来单独用 GB2312 字库来显示,这样在低点阵的时候我们就可以用 GB2312 编码的点阵字库来显示这部分汉字,以获得比较好的显示效果。
  1.4 实现 GB18030 编码转换模块
  上面说了两种显示办法都需要将 GB18030 编码的字符串切割成多段单字节或双字节字符集编码的字符串才能调用相应的字库进行显示。好在 libX11 库中已经提供了一套现成的函数来完成类似的工作,这就是 libX11 中的 lcUTF8.c 模块。这个模块不仅实现了一整套传统字符集编码与UTF-8编码和UCS4编码之间的转换函数,还提供了将 Unicode 编码的字符串切分并转换成多段单、双字节字符集编码的函数。这个模块使用静态的转换码表来实现 Unicode 编码和传统字符集编码之间的转换。与使用 glibc 的 iconv 模块相比它的速度更快,而且占用的内存更小,因为所有 XFree86 软件仅在内存中共享一套转换码表。
  前面已经介绍过,GB18030 编码和 Unicode 编码在字汇上有一一对应的关系,因此它和 UTF-8 编码也同样存在一一对应关系。而 lcUTF8.c 模块所提供的功能正是
相关内容
赞助商链接