CTeX-org / ctex-kit

Macro Packages and Scripts for Chinese TeX users
965 stars 124 forks source link

「元」字在繁簡轉換時變成框([Mapping=han-simp]) #696

Open heangfat opened 4 months ago

heangfat commented 4 months ago

遇到一个怪瑕疵:用繁簡轉換,設置\setCJKmainfont[Mapping=han-simp]{字型名}後,「元」字會變成一个框。複製出來,用 JS 查字串.length字串.codePointAt(),這个框卻根本不存在。 查所用 teckit 之轉換源表,根本沒有「元」(U+5143) 。在該轉換表中加了組映射,將「元」轉換成其它字,也根本轉換不動,仍輸出一个框。換字型,問題依舊,說明不是字型問題。

ZFJ9ZPIW}@F~BEU8@CD)5NE 從 PDF 複製出來,八个字符只剩七个了: 9_F}~9~0BTY0{V2C83%K 0

換字型: 4VQ0I4W0(STO2B }QBTDMJF ZN0ZRUJ `@CG~)Y6BO$BMF

目前僅發現「元」字有此問題,而該字根本不參與繁簡轉換。


備註:

xecjk 之繁簡轉換通過 teckit 實現。teckit 轉換表在 /usr/local/texlive/版本號/texmf-dist/fonts/misc/xetex/fontmapping/xecjk/ 。

teckit 轉換表之編譯工具在此處下載。

muzimuzhi commented 4 months ago

用繁簡轉換,設置\setCJKmainfont[Mapping=han-simp]{字型名}後,「元」字會變成一个框。

\showbox 得到什么信息?

我好像不能复现。等一下,Mapping 不知为何在我这里不管用。你能提供一个完整例子吗?

% !TeX program = xelatex
\documentclass{article}
\usepackage{xeCJK}
\setmainfont[Mapping=han-simp]{FandolSong}

\begin{document}
一元钱
\end{document}

image

查所用 teckit 之轉換源表,根本沒有「元」(U+5143) 。

感觉是因为「元」不需要繁简转换。U+5143 在 Unihan_Variants.txt 里没有提及,见 ./xeCJK/build.lua 第 30、37 行。 https://github.com/CTeX-org/ctex-kit/blob/d897ea427b71787962aa988f219548fba40295e6/xeCJK/build.lua#L29-L38

muzimuzhi commented 4 months ago

我能复现了,之前是 Mapping 没生效。

log 里有一条 "Missing character" 信息(设置 \tracinglostchars=3 可以把它变成 error)。

! Missing character: There is no 𬶃 (U+2CD83) in font FandolSong/OT:language=dfl
t;mapping=han-simp;.

根据信息,元 (U+5143) 被映射到了 (U+2CD83),左鱼右大。 https://zi-hi.com/sp/uni/2CD83 image

% !TeX TS-program = xelatex
\documentclass{article}
\usepackage[LoadFandol=false]{xeCJK}
\setCJKmainfont[Mapping=han-simp]{FandolSong}

\tracinglostchars=3

\begin{document}
一元钱
\end{document}

补充:能找到更多映射错误的字,如,兂 (U+5142) 映射到 U+311CE。使用 STSong 字体,U+5100 - U+51FF 能得到 16 条 "Missing character",我没有查验是不是每一个都是由映射错误、而非字体缺字导致。

heangfat commented 4 months ago

(设置 \tracinglostchars=3 可以把它变成 error)

我這麼設了。也報了相同的錯誤。「元」的確被轉成了「𬶃」(U+2CD83)。

能找到更多映射错误的字,如,兂 (U+5142) 映射到 U+311CE

試了下,「兂」的確會被轉成「𱇎」。

根据信息,元 (U+5143) 被映射到了 (U+2CD83),左鱼右大。

這就奇怪了。han-simp.map 裡並无此映射關係。這些錯誤映射是哪來的?如何解決之? 我在其上級文件夾 misc 內全文搜索,都沒搜到 5143 和 5142。

heangfat commented 4 months ago

我查到問題所在了。han-simp.map 裡有一條U+2CD43 <> U+2CD83,將「𬵃」轉爲「𬶃」。將此句攺爲註釋(在行首加「;」)或刪去,「元」字便正常了。同理,刪U+2CD42 <> U+311CE,「兂」亦正常了。

這明顯不是碼表有誤,而是程序之咎。這種錯誤出在擴展平面,不知是 CTeX 還是 teckit 處理擴展平面碼位的功能不完善?

暫時只能發現一條刪一條。

muzimuzhi commented 4 months ago

不知是 CTeX 還是 teckit 處理擴展平面碼位的功能不完善?

我造了一个「在 plaintex 下使用 \font xetex primitive + mapping」的例子,能复现问题。

猜测问题来源:xecjk 生成的 han-simp.map 格式不对[^1]、teckit 做的 .map -> .tec 转换不对(可能性较小[^2])、xetex 加载并使用 .tec 不对[^3]。

[^1]: 格式需遵循 The TECkit Language: Mapping byte encodings to Unicode

[^2]: 从 https://github.com/silnrsi/teckit/issues/30 推测,teckit 支持 plane 1

[^3]: 相关逻辑似乎在 https://github.com/TeX-Live/texlive-source/blob/trunk/texk/web2c/xetexdir/XeTeX_ext.c

% !TeX TS-program = xetex -interaction=nonstopmode %.tex
\tracinglostchars=3

\def\test{元\Uchar"5143。}

% check that "mapping=<name>" works
\font\1="[FandolSong-Regular.otf]/OT:mapping=fullwidth-stop"\1
\test

\font\1="[FandolSong-Regular.otf]/OT:mapping=han-simp"\1
\test

\end
! Missing character: There is no 𬶃 (U+2CD83) in font [FandolSong-Regular.otf]/OT:mapping=han-simp.
! Missing character: There is no 𬶃 (U+2CD83) in font [FandolSong-Regular.otf]/OT:mapping=han-simp.

image

teckit 轉換表之編譯工具在此處下載。

顺便一提,二进制文件 teckit_compile 随 xetex 分发和安装,应该无需额外下载。可能下载的版本更(gèng)新。

wangweixuan commented 4 days ago

我用 TECkit 提供的 txtconv 工具能复现,所以是 TECkit 自身的问题。

文档和代码(编译器转换器)上看,TECkit 会根据字符所在平面确定一个 pageMap,再用剩下的高八位从 pageMap 里选择一个 characterMap,最后用低八位从 characterMap 里选择映射的目标。pageMap 的元素是 uint8 型的,说明 characterMap 最多存在 256 个;同时每个 characterMap 里字符的低八位一旦相同就会发生冲突。这样来看,考虑到 han-simp 有 6285 条映射,发生冲突的概率是很大的 ($6285/256^2=9.5\,\%$)。

我根据 han-simp.map 找了一下错误映射的字符,在基本平面里有 130 个:

ふブ㒍㒥㖰㗂㛷㜥㝢㟈㣐㣑㣞㣥㣷㤇㧠㨹㩍㫐㫧㬙㭘㲞㲡㲢㲷㴘㴡㶞㷑㷕㷻㸦㹜㿴䁝䂔䂙䂸䃝䄑䆛䈆䈱䋓䎴䒦䗑䚡䜝䞉䡤䤦䥁䧲䩡䪒䫡䯏䯺䱔䳕䴐䷍䷘乲仙侇侍俘偂偈傚傛兂元兮冼剂厖呾啙囡垒壒壖夡婘宙尗峽嵈帏帒店弭弻悠悴惴愝慀懆押挠挫挺掻搯撂撣敢旫晓柆柏柛柳栏栦栽楈槞櫢毡毢毪沥泋

实测这 130 个确实发生错误。包含其他平面的完整的冲突列表请见:Gist