我參與的第一款手遊使用的是自製的成像引擎。內部使用了 FreeType2 來繪製 TTF 文字、實現勾邊/陰影等。開發初期只有考慮橫式由左至右書寫文字的排版方向。產品上線後不知誰給老闆進的讒言說中東地區的玩家所得高、含金量高,若遊戲內語言能支持阿拉伯文肯定有錢景。於是乎就有了以下的研究與實作,提供給大家參考。(註: 最終還是沒有在中東地區上架,因為接洽不到當地營運夥伴)
跟大家科普一個冷知識: 世界上存在的橫式書寫方式共有三種。大部分是採用由左寫到右,少數則是由右寫到左,經典例子為阿拉伯文與希伯來文。還有第三種方式名為「牛耕式書寫」-- 也就是一行左書一行右書來回往復交替,代表例為盲人使用的點字文。
首先列一下我實作時所參考的資料:
第一個是「阿拉伯字母」的維基百科繁體頁面。
再來是對岸「杭州山不高」仁兄在 csdn 上的博文 -- 阿拉伯文排版規則。
從「阿拉伯字母」的維基百科頁內可得知,阿拉伯文的 Unicode Code Point 落於幾個區段:
由百科頁的文字說明,可大約得知阿拉伯文跟英文一樣,是由固定數量 (28個) 的「字母」構成字詞的。但比英文麻煩的是: 各個字母會因為它在字詞裡的位置 (Ex: 字首、字中、或字尾) 、甚至是它前後連接的其它字母,而會產生形狀上的變化。這些「會發生變化」的字母都落在上述的第一區內 (U+0600 ~ U+06FF)。而它們的變化體,都落在「表現形式A」或「表現形式B」這兩個區段中。
當你試著由網頁中複製阿拉伯文字到文字編輯器 (Ex: Word 或 Notepad++ 等等) 中儲存、或是在文字編輯器中直接開啟由網路下載的阿拉伯文文檔時,你會發現兩件事:
- 雖然在顯示上,某些字母是以「表現形式」裡的字形來顯示,但實際在檔案數據中都還是以該字母的未變化形 (第一區、U+0600 ~ U+06FF) 來保存。
- 雖然在顯示上,文字是由右至左來呈現,但字串的數據內容排列其實與非阿拉伯文字串相同 -- 順著閱讀視線,先讀到的字母排在字串前面、後讀到的字母排在後面。
(U+0600 ~ U+06FF 中有些 Code Point 是用來替前一個 Glyph 增添一些筆畫修飾,如 U+064E、U+0652、以及U+0651 等)
雖然 Word 中使用的字型,與維基頁面上用的字型肯定是不同的,因此在筆劃細節上有些差異。即便如此,大家應該還是能對照得出來: 雖然阿拉伯文是由右至左,但字串的數據內容還是跟英文一樣按照字串的閱讀順序來排列的 -- 顯示上在最右邊的字母 (U+0623) 是字串的頭,顯示在最左邊的字母 (U+0629) 是在字串尾。而且,無論字母在顯示時採用何種變化形,在檔案中始終是以未變化形的 Code Point 來保存。
綜合以上幾點,可以得出要進行阿拉伯文字的繪製時,大概的處理過程為:
- 先把要繪製的詞句整個掃過一次,針對所有「未變化」的阿拉伯字母,逐個考慮它在詞中的位置、以及所連接的前後字母,決定它出它的「變化形」,以變化形的 Code Point 取代掉它。
- 將調整過的 Code Points 丟給 FreeType (或同性質的其它 Font Rasterizer),逐個取 Glyph 寬,加總得到整句佔用的像素寬。與單行的最大寬度相比之後決定要不要插入斷行,若必需斷行,則直接像英文句子一樣截掉字串的最尾字詞即可。
- 改變繪製文字時的對齊方式,改為先移到單行的最末,每次繪製 Glyph 時先減去此 Glyph 的 Advance 後再繪製。這樣就可以改為右書繪製。
關於第一步,阿拉伯字母變化形的處理規則,在「杭州山不高」的博文裡已經有詳述了。變化規則就只有兩條,為尊重原創這裡就不轉述了。具體的轉換程式碼我有實作了一份放到這裡可供參考。把 UTF-32 字串丟給 transform() 函數即可得到轉換好的結果 UTF-32 字串。後兩步是排版相關的部分了,上述只提供心法 (Glyph、Advance 為 FreeType 內用語),依據實際使用的 Font Rasterizer 函式庫的不同,會有不同的排版細節要處理,這部分就請見機行事。
最終的驗證方式: 開阿拉伯文網頁,把句子複製貼上到程式中比對顯示結果。阿拉伯語的維基百科是個不錯的選擇。請記得繪製阿拉伯文字時,請選一個與網頁相近的 TTF 字型以便於肉眼比對。Google Fonts 提供了許多阿拉伯字型,是不錯的選擇:
(實際的 TTF 載點位於 Google Fonts 的 Github 頁面中)
沒有留言:
張貼留言