UCS2代码怎么转换为中文啊? 或者中文怎么转换为UCS2代码?
见者有分!

解决方案 »

  1.   

    Turbo Linux公司軟體工程師
    2001 年 9 月GB18030 是最新的漢字編碼字符集國家標準, 向下相容 GBK 和 GB2312 標準。本文將向您講述如何在 XFree86 窗口系統中實現對GB18030 標準的支持。
    1. 簡介1.1 GB18030 簡介
    GB18030 是最新的漢字編碼字符集國家標準, 向下相容 GBK 和 GB2312 標準。 GB18030 編碼是一二四字節變長編碼。 一字節部分從 0x0~0x7F 與 ASCII 編碼相容。 二字節部分, 首字節從 0x81~0xFE, 尾字節從 0x40~0x7E 以及 0x80~0xFE, 與 GBK標準基本兼容。 四字節部分, 第一字節從 0x81~0xFE, 第二字節從 0x30~0x39, 第三和第四字節的範圍和前兩個字節分別相同。 四字節部分覆蓋了從 0x0080 開始, 除去二字節部分已經覆蓋的所有 Unicode 3.1 碼位。也就是說, GB18030 編碼在碼位空間上做到了與 Unicode 標準一一對應,這一點與 UTF-8 編碼類似。目前最新的 glibc 2.2.x 系列已經全面支持了 GB18030 Locale 和 GB18030 與 UCS-4 之間的編碼轉換, 也就是說在系統層上 Linux 已經可以支持 GB18030 標準了。 下面問題的關鍵就是怎樣讓 XFree86 窗口系統也支持 GB18030 標準。1.2 XFree86 國際化原理簡介。
    本文介紹的所有內容都基於 XFree86 4.0.3 及其以後版本, XFree86 早期版本未做測試。XFree86 窗口系統中涉及到國際化的部分主要有字體管理, 文字繪製, 編碼轉換以及進程間通訊。 具體的編程介面請參閱相關資料, 本文不作更詳細的介紹。1.2.1 字體管理與文字繪製
    早期的 XFree86 由於對國際化問題考慮不足, 在字庫介面上僅定義了單字節索引和雙字節索引兩種字庫介面, 分別對應 XDrawString 和 XDrawString16 兩組函數。前一組函數使用8位字模索引序列, 後一組函數使用16位字模索引序列。函數定義分別為:      XDrawString(display, d, gc, x, y, string, length)
                 Display *display;
                 Drawable d;
                 GC gc;
                 int x, y;
                 char *string;
                 int length;       XDrawString16(display, d, gc, x, y, string, length)
                 Display *display;
                 Drawable d;
                 GC gc;
                 int x, y;
                 XChar2b *string;
                 int length; 注意, 兩個函數中的 string 指針指向的並不是編碼序列, 而是字模在字庫中的索引序列。一般將這個序列使用的編碼轉化成為字符集(CharSet)編碼。對於 ISO8859-1 等8位編碼的西方語言來說, 編碼和字符集編碼是一樣的。所以可以直接用 XDrawString 顯示顯示這些編碼的字符串。但對於多字節編碼的複雜語言來說就不一樣了。 以 GB2312 為例, GB2312 編碼(encoding) 的範圍為單字節 0x00~0x80, 雙字節 0xA1A1~0xFEFE, 而 GB2312 字庫的字符集僅覆蓋 GB2312 編碼的雙字節部分, 其編碼是將 GB2312 雙字節編碼中每個字節的第8位置0得來的, 例如, GB2312 編碼為 0xA1A1 的字符的字模(glyph)資訊在 GB2312 字庫中的索引是 0x2121, 而不是 0xA1A1。早期的 XFree86 系統並不支持 ttf 字體, 對於 XFree86 系統中常用的 BDF/PCF 點陣字體而言, 每種字體使用的字符集編碼是固定的, 如中文字庫有 GB2312.1980-0, GBK-0, BIG5-0 等字符集, 日文有 JISX0201。1976-0, JISX0208。1983-0 等字符集。 後來人們在 XFree86系統中引入了對 ttf 等向量化字體的支持, 目前用於 XFree86 4.x 的 ttf 字庫模組有 freetype 和 xtt。 由於絕大多數 ttf 字體都直接使用 UCS2-BE 作為字符集編碼(字庫索引)。 為了和現有 XFree86 系統相容, 這兩個模組都具備編碼轉換功能, 即可以將傳統的字符集編碼轉換為 UCS2-BE 編碼, 從而可以將 ttf 字庫虛擬成傳統的字庫來使用。為了便於字庫管理, XFree86 引入了 XLFD (X Logical Font Discription) 機制來描述字體。 關於 XLFD 的詳悉介紹請參閱 XFree86 的相關文檔。為了增強 X 窗口系統對國際化的支持, 在 X11R5 版本中引進了一系列新的字符串繪製函數和字體集的概念。 簡單來說, 字體集就是將幾種不同字符集的字庫組合成一個覆蓋多個字符集的新字體。例如將一個 ISO8859-1 的英文字庫和一個 GB2312.1980-0 的中文字庫組合成一個字體集, 就可以覆蓋 GB2312 編碼的所有字符了。支持國際化的字符串繪製函數主要分為兩類 XmbDrawString 和 XwcDrawString。 它們的定義為:void XmbDrawString(display, d, font_set, gc, x, y, string,
           num_bytes)
                 Display *display;
                 Drawable d;
                 XFontSet font_set;
                 GC gc;
                 int x, y;
                 char *string;
                 int num_bytes;       void XwcDrawString(display, d, font_set, gc, x, y, string,
           num_wchars)
                 Display *display;
                 Drawable d;
                 XFontSet font_set;
                 GC gc;
                 int x, y;
                 wchar_t *string;
                 int num_wchars;
     前一類用於繪製多字節編碼的字符串, 後一類用於繪製寬字節編碼的字符串。使用這兩個函數可以直接繪製當前系統語言編碼的多字節編碼字符串或者 wchar_t 類型的字符串。但實際上這兩個函數還是將多字節字符串或寬字節字符串拆分並轉換成多段不同字符集的編碼串, 然後調用 XDrawString 或者 XDrawString16 來顯示。所使用的字庫就是字體集中對應的字庫。例如使用 XmbDrawString 繪製 "ABC大家好" 這個GB2312編碼的多字節字符串, XmbDrawString 首先會把這個字符串拆分成兩部分, 一部分是 "ABC" 對應 ISO8859-1 字符集, 將用字體集中的 ISO8859-1 英文字庫來繪製; 另一部分是 "大家好" 對應 GB2312.1980-1 字符集, 將用字體集中的中文字庫來繪製。然後會將這兩個字符串分別轉換為對應字符集的編碼, 轉換後的結果為 "ABC" (未變) 和 0x73 0x34 0x52 0x3C 0x43 0x3A (即 "大家好" 的 GB2312.1980-0 字符集編碼, 也就是字庫索引)。 然後 XmbDrawString 就會分別調用 XDrawString 和 XDrawString16 來顯示這兩個字符串。1.2.2 進程間通訊
    X 窗口系統的進程間通訊主要涉及到不同進程間的文字拷貝和粘貼, 不同進程間的數據通訊以及程式的數據輸入等方面。 由於 X 窗口系統為 Client/Server 結構的分佈式網路窗口系統, 為了在保證進程間通訊的網路透明性和可恢復性的同時, 提供國際化支持, 在 X 窗口系統內部引入了複合文本編碼 (Compound Text)。這個編碼其實是以 ISO-2022(一種早期的多語言編碼框架標準) 為基礎制定的。 它並不是一種真正意義上的編碼, 而僅是一個能將多種不同編碼的字符流組合成一個字符流的框架協議。進程間在使用 Compound Text 通訊的時候可以將各種不同編碼的字符流匯集在一起然後再傳輸, 到達目的進程後再按照一定規則解碼成多個字符流進行處理。 簡單來說, Compound Text 就是利用不同的狀態字符串來標誌各個編碼字符串的起止位置, 利用一種統一的編碼規則對字符串進行編碼, 以免引起歧義。
      

  2.   


    Compound Text 標準中為 ISO8859, EUC-CN, EUC-JP 等許多常用編碼規定了標準的編碼規則和狀態字符串, 另外還定義了幾種擴展規則, 用於將更新的編碼溶入 Compound Text 標準。 Compound Text 標準過於複雜, 而且所能包含的編碼數量也很有限。 隨著 ISO10646/Unicode 標準的不斷發展和完善, X 系統已經開始逐步用 UTF-8 編碼替換 Compound Text。 為了保持與舊系統的相容性, 在 XFree86 系統中 UTF-8 編碼的字符串可以用一種特殊的編碼規則編碼成 Compound Text。1.2.3 編碼轉換
    前面已經提到了很多種文字編碼和標準, 簡單總結如下:多字節/寬字節編碼 (Multibytes/Wide Char): 通俗的說, 多字節編碼就是外碼, 一般為可變長編碼, 主要用於資訊存儲和交換; 寬字節編碼就是內碼, 為定長碼, 通常一個字符對應4個字節, 主要用於資訊處理。 在 POSIX 標準中定義多字節編碼的數據類型為 char, 寬字節編碼的數據類型為 wchar_t。 在目前 linux 常用的 glibc 2.2.x 版本中, 使用 UCS-4 作為寬字節編碼, 並遵循 Unicode 標準 (最新的 glibc 2.2.4 已經完全支持 Unicode 3.1 標準)。 常見的多字節編碼有 UTF-8, ISO8859 系列, GB2312, GBK, EUC-JP 等。 
    字符集 (charset): 即一系列字彙(glyphs)的有序組合。字符集一般使用固定長度的編碼。 常見的字符集有 Unicode (它即定義了編碼的標準, 又定義了字符集的標準), ISO8859-1, GB2312.1980-0, GBK-0 等。 
    複合文本編碼 (Compound Text): 是一種可以將多個不同編碼的字符流匯集在一起的編碼標準, 常用於 X 應用程式之間的進程通訊和文字輸入。 
    既然有這麼多不同的編碼和標準, 那麼 X 系統就必須能夠正確的處理並轉換它們。 在 X 的底層採用了一種模組化的機制來處理所有編碼的轉換與翻譯工作。這些轉換模組被直接編譯到了 X11 Library 中 (具體源代碼請參閱 XFree86 源碼中的 xc/lib/X11/lc* 等文件)。首先, 在 X11 Library (以下簡稱 libX11) 中為它所支持的編碼標準定義了標準名稱 (見 XlcPublic。h 文件):#define XlcNMultiByte           "multiByte"       /* 多字節編碼 */
    #define XlcNWideChar            "wideChar"        /* 寬字節編碼 */
    #define XlcNCompoundText        "compoundText"    /* 複合文本編碼 */
    #define XlcNString              "string"          /* 傳統字符串編碼, 即 ASCII 編碼 */
    #define XlcNUtf8String          "utf8String"      /* UTF-8 編碼 */
    #define XlcNCharSet             "charSet"         /* 字符集編碼 */
    #define XlcNCTCharSet           "CTcharSet"
    #define XlcNFontCharSet         "FontCharSet"     /* 字庫使用的字符集編碼 */
    #define XlcNChar                "char"            /* 單個字符 */
    #define XlcNUcsChar             "UCSchar"         /* UCS-4 編碼的字符 */
     其中 XlcNCharSet 和 XlcNFontCharSet 在絕大多數情況下是一樣的, 目前僅在處理 Unicode 的時候有區別。 在 UTF-8 Locale 下, XlcNCharSet 使用的是 UTF-8 編碼, 而 XlcNFontCharSet 使用的是 UCS-2 Big Endian 編碼。在程式初始化的時候, libX11 將調用 _XlcInitLoader() 函數來初始化所有的編碼轉換模組, 程式段如下:void
    _XlcInitLoader()
    {
    #ifdef USE_GENERIC_LOADER
        _XlcAddLoader(_XlcGenericLoader, XlcHead);
    #endif#ifdef USE_DEFAULT_LOADER
        _XlcAddLoader(_XlcDefaultLoader, XlcHead);
    #endif#ifdef USE_UTF8_LOADER
        _XlcAddLoader(_XlcUtf8Loader, XlcHead);
    #endif#ifdef USE_EUC_LOADER
        _XlcAddLoader(_XlcEucLoader, XlcHead);
    #endif#ifdef USE_SJIS_LOADER
        _XlcAddLoader(_XlcSjisLoader, XlcHead);
    #endif#ifdef USE_JIS_LOADER
        _XlcAddLoader(_XlcJisLoader, XlcHead);
    #endif#ifdef USE_DYNAMIC_LOADER
        _XlcAddLoader(_XlcDynamicLoader, XlcHead);
    #endif
    }
     在以上所有轉換模組中, Generic 轉換模組 (_XlcGenericLoader) 是最重要的一個模組。 它使用 XLC_LOCALE 描述文件所定義的標準轉換方法完成 XlcNWideChar, XlcNCharSet, XlcNCompoundText 等編碼之間的相互轉換。 Utf8 轉換模組 (_XlcUtf8Loader) 則專門用於 UTF-8 Locale 下的各種編碼轉換。 Dynamic 模組用於動態調入獨立的編碼轉換模組。 但在目前的 XFree86 4.x (X11R6.5) 中, 這個模組是無效的。下面就以 Utf8 模組 (lcUTF8.c) 為例, 來介紹一下編碼轉換模組的構造:雖然常用的編碼標準僅有 XlcNCharSet, XlcNMultiByte, XlcNWideChar 等幾種, 但經過排列組合, 需要處理的編碼轉換還是非常多的。 下面僅以 XlcNCharSet 到 XlcNMultiByte 轉換為例來說明問題。首先, 在 lcUTF8.c 中定義了一個負責將 XlcNCharSet 編碼的字符串轉換為 XlcNMultiByte 的字符串的函數:(注意, 在 UTF-8 Locale 中, MultiByte 編碼即為 UTF-8, 所以 XlcNMultiByte 和 XlcNUtf8String 其實是一樣的)/* from XlcNCharSet to XlcNUtf8String */static int
    cstoutf8(
        XlcConv conv,
        XPointer *from,
        int *from_left,
        XPointer *to,
        int *to_left,
        XPointer *args,
        int num_args)
    {
    ....... (略)
    }
     關於數據結構 XlcConv 的定義請參見 XlcPublic。h 文件。然後定義了一個 XlcConvMethodsRec 對象和一個初始化函數:static XlcConvMethodsRec methods_cstoutf8 = {
        close_converter,
        cstoutf8,
        NULL
    };static XlcConv
    open_cstoutf8(
        XLCd from_lcd,
        const char *from_type,
        XLCd to_lcd,
        const char *to_type)
    {
        lazy_init_all_charsets();
        return create_conv(from_lcd, &methods_cstoutf8);
    }
     最後還要在初始化函數 _XlcAddUtf8Converters() 和 _XlcUtf8Loader() 中分別加上:_XlcSetConverter(lcd, XlcNCharSet, lcd, XlcNUtf8String, open_cstoutf8);和:_XlcSetConverter(lcd, XlcNCharSet, lcd, XlcNMultiByte, open_cstoutf8);
     至此, UTF-8 Locale 的 XlcNCharSet 到 XlcNMultiByte 和 XlcNUtf8String 的轉換函數就完成了。 當 X 應用軟體在 UTF-8 Locale 下需要進行 CharSet 到 MultiByte 或 Utf8String 轉換的時候, libX11 會自動調用 函數 cstoutf8 進行轉換。大家讀過 lcUTF8.c 後也許會發現裏面並沒有定義 Compound Text 和其他編碼之間的轉換方法。 其實在 lcCT.c 中已經定義了 Compound Text 和各種標準 CharSet 以及已在 XLC_LOCALE 中定義的非標準 CharSet 之間的轉換函數, libX11 會利用這些函數完成 MultiByte, Utf8String 等編碼和 Compound Text 之間的間接轉換。由於 libX11 中所有的編碼轉換都倚賴於這些編碼轉換模組, 所以要想讓 libX11 支持一種新的編碼(即一種新的 Locale), 就必需在 libX11 中加入相應的編碼轉換模組。 在 X 窗口系統中實現對 GB18030 的支持的難點就在於此了。
      

  3.   

    Turbo Linux公司軟體工程師
    2001 年 9 月本文是《在 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 模組所提供的功能正是我們的 GB18030 編碼轉換模組所需要的。所以很自然的就可以想到我們可以在 lcUTF8.c 的基礎上實現 GB18030 編碼轉換模組。實際上我們並沒有編寫獨立的 GB18030 模組
      

  4.   


             /* 調用 glibc 中的 wctomb 函數將 UCS4 字符轉換成 GB18030 多字節字符 */
            count = wctomb(dst, wc);
            if (count == 0)
                break;
            if (count == -1) {
                count = wctomb(dst, BAD_WCHAR);
                if (count == 0)
                    break;
                unconv_num++;
            }
            src += consumed;
            dst += count;
        }    *from = (XPointer) src;
        *from_left = srcend - src;
        *to = (XPointer) dst;
        *to_left = dstend - dst;    return unconv_num;

    另外,lcUTF8.c 中原先是沒有 GBK<->UCS4 和 BIG5-HKSCS<->UCS4 轉換模組的,我們參照 xc/lib/X11/lcUniConv 目錄中其他轉換模組的格式為 lcUTF8.c 增加了這兩個模組。至此,我們就在 libX11 中實現了 GB18030 的編碼轉換模組,XFree86 的 GB18030 內功已經修煉完成,下面就要開始修煉 Locale 描述文件、字庫介面等外功了。2. GB18030 Locale 的 XLC_LOCALE 文件
    *nix 系統中編寫國際化程式的關鍵就是底層 libc 庫對 locale 的支持。每一種語言編碼和國家/地域都對應一個 locale,其中提供了編碼轉換資訊、日期貨幣格式、字符排序規則等資訊。同樣,要想讓 XFree86 支持一個 locale,就必須有一個相應的 XLC_LOCALE 文件,其中描述了這種 locale 使用的文字編碼、所有字符集編碼以及字體集等資訊。GB18030 locale 對應的 XLC_LOCALE 文件內容如下: 
    #     XFree86 NLS for Chinese encoding GB18030
    #                Modified from xc/nls/XLC_LOCALE/en_US.UTF-8
    #            by James Su # 
    #        XLC_FONTSET category

    XLC_FONTSETon_demand_loading        Trueobject_name              generic#        We leave the legacy encodings in for the moment, because we don't
    #        have that many ISO10646 fonts yet.#        以下定義了 GB18030 locale 使用的所有字庫和對應的字符集編碼
    #        字體集 fs0, fs1 對應單字節部分的 0x0~0x7F 和 0x80~0xFF
    #        fs0 class (7 bit ASCII)
    fs0      {
             charset         {
                     name        ISO8859-1:GL
             }
             font        {
                     primary         ISO8859-1:GL
                     vertical_rotate         all
             }
    }#        fs1 class (ISO8859 families)
    fs1      {
             charset         {
                     name        ISO8859-1:GR
             }
             font        {
                     primary         ISO8859-1:GR
             }
    }#        字符集 fs2, fs3 對應其他部分
    #        fs2 class (Chinese Han Character)
    fs2      {
             charset         {
                     name        GB2312.1980-0:GL
             }
             font        {
                     primary         GB2312.1980-0:GL
             }
    }#        fs3 class
    fs3      {
             charset         {
                     name        ISO10646-1
             }
             font        {
                     primary         GB18030-0
                     substitute GBK2K-0
             }
    }
    END XLC_FONTSET# 
    #        XLC_XLOCALE category

    XLC_XLOCALEencoding_name                GB18030
    mb_cur_max               4
    state_depend_encoding        False#        下面定義的是 GB18030 locale 使用的所有字符集的資訊
    #        cs0 class
    cs0      {
             side                GL:Default
             length              1
             ct_encoding         ISO8859-1:GL
    }#        cs1 class
    cs1      {
             side            GR:Default
             length          1
             ct_encoding     ISO8859-1:GR
    }
     
    #        cs2 class
    cs2      {
             side            GR
             length          2
             ct_encoding     GB2312.1980-0:GL; GB2312.1980-0:GR
    }#        cs3 class
    cs3      {
             side                none
             ct_encoding         ISO10646-1
    }END XLC_XLOCALE
     
    注意字符集 fs2 的定義,字符集編碼使用的是 ISO10646-1,也就是 UCS2-BE,而字體是 GB18030-0。其實 GB18030-0 只是我們在 xtt 字庫模組中增加的一個 ISO10646-1 的別名而已,目的是將 GB18030 字庫與其他 ISO10646-1 字庫區別開來。字符集定義 cs0 ~ cs3 與字體集 fs0 ~ fs3 並沒有直接的聯繫,它們的定義是獨立的。定義 cs0 ~ cs3 的用途是告訴 libX11,在將 GB18030 字符串編碼成 Compound Text 前先拆分成使用 ISO8859-1:GL,ISO8859-1:GR,GB2312.1980-0 以及 UTF-8 等不同字符集編碼的四段字符串,然後在編碼成一個 Compound Text 數據流。這樣做的好處是,我們可以從 GB18030 locale 的應用程式向 GB2312 locale 的應用程式傳輸英文字符和 GB2312 中文字符;或向英文 locale 的應用程式傳輸純 ASCII 碼數據。現在 XFree86 支持 GB18030 的工作已經基本完成,最後就差在 xtt 字體模組中添加對 gb18030-0 以及 gb18030.2000-0 和 gb18030.2000-1 的支持了。3. 改造 xtt 模組
    XFree86 中包含了兩個常用的 TrueType 字體模組,xtt 和 freetype。相對而言,freetype 模組使用標準碼表來實現對各種字符集編碼的支持,改造起來相對簡單些。但由於性能以及功能較 xtt 弱,所以人們常用 xtt 模組。xtt 模組採用編碼轉換模組來支持各種編碼的字體。所有編碼轉換模組都被編譯成動態庫文件,可以在使用的時候動態載入和釋放。下面就介紹一下怎樣讓 xtt 支持上述三種字體。支持 gb18030-0 非常簡單,因為它就是 ISO10646-1 (UCS2-BE編碼)的一個別名而已。支持 gb18030.2000-0 也比較簡單,只需把 xtt 原有的 GBK 模組拿來稍加改造就行了。gb18030.2000-1 就比較複雜了,因為它是 mozilla 專用模組,採用採用順序編碼的方式把 GB18030 的四字節部分重新編碼,我們必須建立這種新編碼和 UCS2-BE 編碼之間的映射表才能編寫 gb18030.2000-1 模組。好在 mozilla 的源碼中有這張表,只需要轉換一下格式就行了。按照 xtt 中其他編碼轉換模組的格式,我們可以將三種字體的代碼寫到一個模組中,主程式(main.c)的代碼如下: 
    /* ===EmacsMode: -*- Mode: C; tab-width:4; c-basic-offset: 4; -*- === */
    /* ===FileName: ===
       Copyright (c) 1998 Takuya SHIOZAKI, All Rights reserved.
       Copyright (c) 1998 X-TrueType Server Project, All rights reserved. 
       (省略註釋)
    Notice===
     */#include "xttversion.h"static char const * const releaseID =
        _XTT_RELEASE_NAME;#include "xttcommon.h"
    #include "xttcap.h"
    #include "xttcconv.h"
    #include "xttcconvP.h"typedef enum
    {
        GB18030_0,
        GB18030_2000_0,
        GB18030_2000_1
    } CharSetMagic;static CharSetRelation const charSetRelations[] = {
        { "gb18030",  "2000", "0", GB18030_2000_0, { 0x40, 0xff, 0x81, 0xfe, 0x8140 } },
        { "gb18030",  "2000", "1", GB18030_2000_1, { 0x00, 0xff, 0x00, 0x99, 0x0000 } },
        { "gb18030",    NULL, "0", GB18030_0,      { 0x00, 0xff, 0x00, 0xff, 0x3000 } },
        { "gbk2k",      NULL, "0", GB18030_0,      { 0x00, 0xff, 0x00, 0xff, 0x3000 } },
        { NULL, NULL, NULL, 0, { 0, 0, 0, 0, 0 } }
    };CODECONV_TEMPLATE(cc_gb18030_2000_0_to_ucs2);
    CODECONV_TEMPLATE(cc_gb18030_2000_1_to_ucs2);
    CODECONV_TEMPLATE(cc_font_gbk2k_to_ucs2);
    static MapIDRelation const mapIDRelations[] = {
        { GB18030_2000_0,  EPlfmISO,     EEncISO10646,
                                         cc_gb18030_2000_0_to_ucs2,      NULL },
        { GB18030_2000_0,  EPlfmUnicode, EEncAny,
                                         cc_gb18030_2000_0_to_ucs2,      NULL },
        { GB18030_2000_0,  EPlfmMS,      EEncMSUnicode,
                                         cc_gb18030_2000_0_to_ucs2,      NULL },
        { GB18030_2000_1,  EPlfmISO,     EEncISO10646,
                                         cc_gb18030_2000_1_to_ucs2,      NULL },
        { GB18030_2000_1,  EPlfmUnicode, EEncAny,
                                         cc_gb18030_2000_1_to_ucs2,      NULL },
        { GB18030_2000_1,  EPlfmMS,      EEncMSUnicode,
                                         cc_gb18030_2000_1_to_ucs2,      NULL },
        { GB18030_0,       EPlfmISO,     EEncISO10646,
                                         cc_font_gbk2k_to_ucs2,          NULL },
        { GB18030_0,       EPlfmUnicode, EEncAny,
                                         cc_font_gbk2k_to_ucs2,          NULL },
        { GB18030_0,       EPlfmMS,      EEncMSUnicode,
                                         cc_font_gbk2k_to_ucs2,          NULL },
        { -1, 0, 0, NULL, NULL }
    };STD_ENTRYFUNC_TEMPLATE(GB18030_entrypoint)ft_char_code_t /* result charCodeDest */
    cc_font_gbk2k_to_ucs2(ft_char_code_t codeSrc)
    {
        return codeSrc;
    }
    /* end of file */
     
    可以看到 gb18030-0 到 UCS2-BE 的轉換函數就是原封不動的將數據返回。gb18030.2000-0和gb18030.2000-1的轉換函數由於太長就不列出來了。至此,我們對 XF