有个思源宋体的字体使用疑惑,请大佬指点

2023-08-16 15:33:31 +08:00
 bard99

U+2f04 ⼄

U+4e59 乙

在使用 itext 的 html2pdf 生成 PDF 文件,发现一个奇怪的问题。 举个例子,我的原始文本是:乙方。这个乙方就是输入法正常打出来的乙方,拼音:yifang 。Unicode 为 U+4e59 。

但是生成的 PDF 文件,却变成了:⼄。Unicode 为 U+2f04 。拼音查不出来,好像因为是一个部首?

我是用的思源宋体的粗体,然后产生了这个问题。后面我换了一个字体,狮尾四季春。PDF 生成的文字就正常了,生成的也是乙方。拼音:yifang 。Unicode 为 U+4e59 。

我去查了一下,确实这是 2 个字符:

但是思源宋体也是同时有这 2 个字符:

那导致这个转换不一致的原因是什么呢?

  1. 我看了下 html2pdf 的源码 debug ,找了半天没找到关于字体这里转换字符的逻辑。
  2. 但是我想了下,原始文本是乙方,乙的 Unicode 是 U+4e59 ,那应该 html2pdf 在思源宋体里面找 U+4e59 编码的字符使用就好了,但这里不应该会找错啊?

困扰了半天了,求助下了解的大佬~

2257 次点击
所在节点    Java
17 条回复
tabris17
2023-08-16 16:00:10 +08:00
应该是 iText 的 bug
我用字体工具查看了思源宋体,发现代码点$2F04,$4E59 共用一个字形,可能 iText 在处理字体输出后就取了小的那个 Unicode 码
NessajCN
2023-08-16 16:05:41 +08:00
你是怎么查看转换完的 pdf 的字符编码的?
bard99
2023-08-16 16:13:10 +08:00
@tabris17 大佬,怎么看是共用了一个字型呢?
bard99
2023-08-16 16:14:56 +08:00
@NessajCN 直接复制 PDF 的文本出来,然后就可以查询了:
我帖子里用的是 birdfont: https://birdfont.org/
mw2c
2023-08-16 16:15:45 +08:00
https://juejin.cn/post/6844903729439703053
https://gist.github.com/gettalong/d894c29b551917573c708cd5110653a0
找到这两篇文章,猜测是生成 PDF 时 itext 对文字进行了 NFC Normalization 处理
tabris17
2023-08-16 16:15:46 +08:00
@bard99 https://font.qqe2.com/ 在线字体编辑器
bard99
2023-08-16 16:18:51 +08:00
@tabris17 难道上面截图里,表示的是 2f04 引用了 4e59 的字型?感觉可以说得通啊!
bard99
2023-08-16 16:21:05 +08:00
@tabris17 #6
看到了,果然共用了字型!
NessajCN
2023-08-16 16:22:23 +08:00
@bard99 你从 pdf 里直接拷贝是不能准确到编码的,只能靠识别,所以就是你 copy 的时候识别错了
bard99
2023-08-16 16:31:33 +08:00
@NessajCN #9 感谢指正,试了下确实如你所说。 实际上我中间省略了一些过程,我是 debug 过程中,使用 itext 的 PdfReaderContentParser 读取生成后 PDF 文件,然后取出文本内容,在这里拿到了异常的 ⼄⽅ u2f04 。
uleh
2023-08-16 16:32:18 +08:00
@NessajCN 文本型的 PDF 是可以直接复制的。全图的那种才是 OCR 识别
NessajCN
2023-08-16 16:37:59 +08:00
@bard99 pdf 呈现内容的方式是这样的:拿到字体绘制逻辑,然后根据实际的字显示字形。而 pdf 文件里本身只会存这种「显示方式」也就是只会存字形,除非它另外也存了一份 toUnicode 映射,这个是可存可不存的。所以你 copy pdf 出现编码不对是很正常的。这是 pdf 为了应付盗版山寨而特意为之的行为。
bard99
2023-08-16 17:30:13 +08:00
@mw2c 这 2 篇文字涨知识了,感谢。然后我本地试了一下 4 种正规化的结果,打印如下:

这个结果看上去,好像可以得出:
- 只有「部首」字符(如:2f04 )在正规化后,可以变成正常的「单音」字符(如:4e59 )。
- 而「单音」字符的正规化,得到的仍然是它本身。

如果这 2 个结论对的话,在我的场景里,却是「单音」字符,转换后得到了「部首」字符,那就不符合这个结论了。
tabris17
2023-08-16 17:41:55 +08:00
@bard99 如果是 NFC 的话,不至于换一个字体就正常了。

可以查看一下狮尾四季春字体,2F04 和 4E59 两个字有没有共用一个字形,如果不是,则 BUG 的可能性更大一些
mw2c
2023-08-16 18:01:08 +08:00
@bard99 不好意思,我没仔细看帖子。应该是和 PDF 嵌入字体的方式有关。找到另一篇文章,作者也是换了一个字体就正常了。作者最后也有补充:
```PDF 内部存储文字并不是以 Unicode 的方式,而是有独立的编码方式 ANSI 、Identity-H 等。然后在用户复制文字的时候通过/ToUnicode 转换出 Unicode 编码。```
https://xobo.org/unicode-normalization-nfd-nfc-nfkd-nfkc/
bard99
2023-08-16 19:09:54 +08:00
@tabris17 #14 刚用 fontstore 试了狮尾四季春、SiHei 字体,发现他们都没有给 2F04 编码添加字型!所以这 2 个字体生成的 PDF ,文字都是正常的,都是 4E59 编码的「乙」!
bard99
2023-08-16 19:59:40 +08:00
@mw2c #15 @tabris17
我刚才做了几个实验,猜测找到了原因:

我使用 WPS 先生成 word ,再将 word 转成 PDF 。使用的是思源宋体:


然后使用 Python 脚本读取 PDF 内容和 Unicode 编码值:

发现 WPS 生成 PDF 的文字是正常的「 4e59 」编码!

对比之下,我之前使用 itext 生成的 PDF 。使用的是思源宋体:

使用 Python 脚本读取 PDF 内容和 Unicode 编码值:

这个 PDF 的文字是异常的「 2f04 」编码!

所以猜测可能就是 itext 的 bug:
1. 对于共用字型的字体,如:2f04 和 4e59 ,字型为「⼄」和「乙」。
2. itext 程序的 bug ,导致了在使用 html 生成 PDF 的过程中
3. 首先 html 文本传入的是 4e59 ,然后 itext 根据 4e59 找到了字体「乙-2f04/4e59 」
4. 然后写入生成 PDF 的过程中,使用了「乙」的字型,但错误的使用了 2f04 的 Unicode 编码!
a. 对于 WPS 来说没有这个问题,会使用 4e59 的 Unicode 编码。
5. 所以原因,还是 itext 的问题! [至于具体的原因,还得 debug 去看了;看了半天没看出来逻辑在哪里...] !
a. 初步推测,itext 根据 4e59 找到了字体「乙-2f04/4e59 」,然后写入 PDF 时写入了「乙-2f04 」,很可能就是获取了第一个 Unicode 编码值!而 WPS 可能是写入了「乙」,然后 Unicode 编码值则从原始文本获取,这样就关联正确了!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/965813

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX