iT邦幫忙

2023 iThome 鐵人賽

DAY 13
1
SideProject30

一起成為新世紀文字藝術師:深入玩轉 Unicode 和 OpenType系列 第 13

DAY 13|OpenType Feature (6):組合字符

  • 分享至 

  • xImage
  •  

到目前為止,無論是把一換一(smcp, vert, calt, swsh, ss01)或是多換一(liga, hlig, dlig),都是「字符」對「字符」之間的變化——那如果是「變音符號(Diacritic)」與「字符」呢?

依舊拿法文和德文來舉例,像是有一撇(尖音符,Acute)的 é 和有兩點(元音變化符,Umlaut)的 ü 又該怎麼處理?

在原始的 ASCII,也就是英文字母裡,是不存在這些變音符號的(至於為什麼在英文裡沒有使用變音字母呢?這可是另一個語言學的大問題),但隨著編碼的擴充,為了配合不同的語言需求,便在拉丁字母擴展區中塞入了不同的變音字母,例如上面提到的 é 或是 ü

不過,Unicode 很快就意識到,收錄所有變音字母的想法實在太過天真,畢竟變音符號這種在任合字母上都可以加上去的東西,有時候還不僅限於只加一個,有需要的話可以無限的上疊,產出的組合可以説成千上萬——光是一個 a,就 至少 可以組出 áăắặằẳẵǎâấậầẩẫäạàảāąåǻã 共 23 種變化——甚至,你也可以在漢字上面標記變音字母,雖然沒有人會這樣,但也沒規定不行這樣做。

字符分解與組合(Glyph de/composition, ccmp)

因此,Unicode 在 U+0300 - U+036F 區域預作變音符號,允許任意字母和這一區的變音符號進行組合。

這一區的變音符號除了上面提及的拉丁字母之外,也收錄了希臘字母的變音符號、日文的濁點與半濁點、希伯來文的尼庫德(Niqqud)記號等。

透過 ccmp,我們便可以「製作」出沒有被收錄在 Unicode 裡面的變音字母,例如台羅拼音的第八調調號字符。因此,ccmp 也是一種替換。

通常來說,軟體與瀏覽器會強制使用 ccmp,只要有好好設定(言外之意就是很多字型其實都沒有正確的設定),字符與變音符號就能透過該表進行「合體」。

.class {
  -moz-font-feature-settings: "ccmp";
  -webkit-font-feature-settings: "ccmp";
  font-feature-settings: "ccmp";
}

但為了讓那些不支援 OpneType 的陽春文書軟體亦能正常顯示,變音符號的字符寬度通常會設為 0,讓變音符號可以和字符相貼在一起。即使沒辦法讓變音符號出現在正確的位置,但至少可以大致上知道該字是一個變音字母,不至於造成歧異。

無限變音符號

此外,變音符號的使用不僅限於一個,每次組合起來的變音字母都可以在和新的變音符號再組合,理論上,是可以無限添加在子母上的。有時候會在網路上看到像是 「Ȟ͙̰̜̠͚̈̀̈̓̋̀̾̐e̱͔̝̞̬̘̩̽̋͋̑̀̌̆͊̐̋l̝̝͕̦͇̪͚̱͙̲̠̦͊̆̋́̇l̰̥̞͓͙͇̬̦̽̃̐͂̒̿͊̒͊̃̌̾ǒ͎̞̰̝̬̥͎̝̞͔̖̏͊͛̊͂̀̆͗͛͗̄ W̖̗̬͓̤͚̞̋́͊͆̇̇͑̓̈̾̚ͅo̳̲̤̩̭͇̓́̏̎̀r̥̣͓͍̊́̏̾̾́̌̐d͓̰̥̠͕͉͇̜̝́̎͌̉̾̈́̉ͅ」這樣的迷惑字母(稱作 Zalgo),就是運用了符號組合的 ccmp

韓文?

在現代韓文字母(Hangul Jamo)中,共有 19 個初聲(자음,子音)、21 個中聲(모음,母音)、28 個終聲(종성,27 個子音組合以及「沒有」),排列組合之後總共有 192128 = 11,172 種可能。

在這 11,172 個字母組合中,又有許多發音饒舌、結構奇葩的韓文是不會使用到的,實際生活中大概只會看到 1,200 多種字母。舉例來説, [ppal] 應該已經是目前正在使用的韓文裡最複雜的了(빨다 - 「洗」的名詞形);更複雜的像是 [jjwaelt] (我也不會念),就只是在拼字邏輯上合理、但現實生活不可能用到的字。

面對這 11,172 的組合,如果使用 ccmp 實現組合,便只需要佔用 19+21+28=68 個碼位就好,那為何 Unicode 還是為每個可能出現的韓文都分配了單獨的碼位?雖然有謎之八卦指出是韓國的施壓,但我想還是為了配合當初韓國廠商施行已久的 KS X 1001 編碼、以及當年 OpenType 支援度還未普及的關係。

在歷史上,1991 年最初釋出的 Unicode 1.0 中,在 U+3400 - U+3D2D 區域收錄了 2350 個常見韓文、隔年 1.1 又再增加 4306 個韓文(佔用 U+3400 - U+4DFF)。這個時間點剛好是 OpenType 剛誕生的時候,對組合的支援性還未普及,因此 Unicode 最後還是決定獨立收錄所有組合結果。於是,在 1993 年 的 2.0 版本中,韓文被搬遷到 U+AC00 - U+D7AF 區域。原本的碼位改收錄 CJK 擴充 A 和易經八卦符號——這也是為何擴 A 的碼位會比基本區還前面的原因,也是 Unicode 極少更改碼位的事件。

當然,這不代表我們不能用 ccmp 來組韓文,如果你瀏覽器的字型(像是思源系列)有支援 OpenType ccmp 的話,U+AC01)和 각U+1100 / U+1161 / U+11A8)看起來就 應該要是一樣的。反之,若你看到後面的 [gag] 是三個字母,就代表你的字型並沒有特別映射 ccmp

至於更複雜,至多包含 124 個初聲、94 個中聲、137 個終聲、2 個旁點,理論上可以同時合併九個部件,多達 1,638,750 種組合可能的古韓文與方言...恩...面對連 Unicode 都放不下的數量,就只能先使用 ccmp 組合、再使用 Unicode 裡額外的 ljmo (Leading consonant Jamo)、vjmo (vowel Jamo) 和 tjmo (trailing Jamo) 特性顯示了——其中大多的結果只是為了展現韓文的組合邏輯和可組合性,歷史上根本不會使用到。

歷史遺留問題

目前,因為我們已經可以透過變音符號區自由的組合出字母,因此 Unicode 已經明確的表示不會再收錄新的變音字母。不過,那些在早期已經收錄的字母,因為 Unicode 原則的 Stability,其碼位也不會被移除。

此時,就有可能遇到一個問題,在早期被收錄在 Unicode 裡面的變音字母,例如 éU+00E9),其實也可以透過組合的方式,以 eU+0065)與 U+0301 來表示。雖然兩者「看起來」是一樣的,但記憶體裡儲存的碼位其實是不同的。

因此,若要執行搜尋與比對,和前面提到的未統一漢字相同,必須要將兩者透過正規化方法進行整併。


上一篇
DAY 12|OpenType Feature (5):豎排
下一篇
DAY 14|OpenType 標記語言:AFDKO
系列文
一起成為新世紀文字藝術師:深入玩轉 Unicode 和 OpenType30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言