
前两天,编辑 @漢三 在 Slack 上发给我一个 PDF 文件,问我知不知道为什么从上面复制出的英文会出现「重字」现象。他还说过,这个难题只在用功能自带的预览 app 打开时会出现,用其余 PDF 阅读器复制文字是正常的。
用预览 app 复制出的「结巴」文字
文刀拿这个难题来问我,恐怕是因为我之前写过一篇 解释 PDF 格式的标题,觉得我大约会明白答案。不过他同样高估了我的常识水准——我刚开始也不知道这是为啥回事。不过,经过一番搜索,我最后初步搞清楚了难题成因。因为这个难题涵盖到一些很有意思的细节,这里把探索的过程写出来,供有类似疑问的朋友参考。
出现难题的 PDF 文档是由 一篇少数派文章 导出而得的。首先尝试复现问题:用预览 app 打开并随手复制一段,确实出现了好多重复的插图,看起来就像是「结巴」了一样,有一种莫名的喜感。换用 PDF Expert 打开再尝试复制,则没有这样的难题。

虽然不知道问题的详细成因,但按照经验,文字复制中的故障常常与编码有关,而 PDF 格式正是编码难题的富户。
我在之前的标题中提及,PDF 格式是「不识字」的。在表明插图时,阅读器只是机器地按照 PDF 语句的命令,将笔画资源中特殊码位的字形绘制在座标指定的位置,而并不关心自己画出来的究竟是多少字。只有在进行复制、搜索等操作时,PDF 才会按照内嵌的 CMap,将外部字体的编码和 Unicode 编码对应起来。因此,如果 CMap 缺失或损坏,就无法从 PDF 中正常复制文字,但并不影响文件的性能。
PDF 中文本编码的方法
这次的难题会不会也跟 CMap 有关呢?这就要察看 PDF 的代码才能明白。不过,大多数 PDF 都经过压缩,用文本编辑器直接打开是不可读的。为此,我们首先用 qpdf 将其解压:
qpdf --stream-data=uncompress sspai.pdf sspai_unzip.pdf
这样,就可以用任意文本编辑器打开查看源码了。但可是这般,PDF 的代码结构也很纷乱,从头翻开很难找到头绪。因此,我们可以从找准一个小处入手——例如文章中的这个「工」字。
查询 Unicode 字符表,可以明白「工」字的编码是 U+5DE5。既然 PDF 中有「工」字,那么代码中的某处一定会讲到 5de5 这个编码。确实,简单搜索一下就可以寻找怎么一段:
1 beginbfchar
<0ae1><2f2f 5de5>
endbfchar
其中,beginbfchar 和 endbfchar 正是 CMap 所用的语法。根据 PDF 语法,它显示 PDF 内嵌字体中编码为 0ae1 的字形,对应 Unicode 编码为 U+2F2F 和 U+5DE5 的两个字符。
PDF 源码中提到的「工」字 Unicode 编码
奇怪了,为什么是两个字符?
我们已然明白,U+5DE5 就是汉字「工」,那这个多出来的 U+2F2F 又是多少?再次 查询 Unicode 表,会发现 U+2F2F 竟然也有「工」。这两个字符是多少关系?它们是一回事吗?
答案能否定的。仔细看一下两个字符的 Unicode 信息:U+5DE5 的全称是「Ideograph labor, work; worker, laborer CJK」,位于 CJK Unified Ideographs(中日韩统一表意文字)区块。显然,这就是我们日常所用的小篆「工」。
另一方面, U+2F2F 的名字是「Kangxi Radical Work」,其所在区块是「Kangxi Radicals」(康熙字典部首)。换句话说,两个「工」虽然长得一模一样,但身份并不相似,一个是汉字,另一个是偏旁。
这样一来,我们也就明白预览 app 是为啥复制出「重复」的插图了。
实际上,它并没有复制出来两个一样的「工」字,而是同样复制出来了两个不同的符号——前一个是成为偏旁的「工」,后一个才是真正的小篆「工」。将预览的复制结果粘贴到 文本转 Unicode 编码工具 里,就能看得很明显:
看似重复的插图实际上是两个不同的符号
不过,PDF 为什么要在 CMap 中作这么「一对多」的映射呢?的确,从预览 app 的角度看,它恐怕还有事实觉得一点小委屈:我做错了怎么,PDF 明明告诉我这是两个字啊?
回答这个难题,就要先了解「康熙字典部首」区块中的符号的用途。对此,这个网站 解释得很清楚(原文为简体日文;粗体为笔者所加):
既然全部部首的字元都在另一个地方编了码,那么何以要随后编码呢?据统一码(即 Unicode——笔者注)的文档强调,本区块的字元只作偏旁之用,不可能当做一般文字用途,文件更进一步提出,必要时甚至可以用不同的字型格式,表明是属于本区块的字元。换句话说,例如编辑一本字典,部首页、部首标题和「参见某部若干画」等文字,都应使用本区块内的字元;而内文和字头、词条等文字部分,则应使用「中日韩统一表意文字区块」中的字元。这样做的本意,是能够让机械知道该字元现时所充当的配角:是「一般文字」,还是「部首文字」。当然,这些分别对动物来说可能没有作用,但对机械的语意分析是更加重要的。
实际上,类似的需求在使用拉丁符号的修辞中毕竟存在。最典型的是西文排版中何谓 「合字」(ligature)的定义,即针对 ff、fi 这样字体容易「打架」的符号组合,将其当做一个整体来专门设计外形。因此,这些符号组合在 Unicode 中需要有独立的码位pdf怎么不能复制文字,如 (U+FB00)、 (U+FB01) 等(请试着动手复制一下这些符号)。可是,合字在表明时是一个整体,而在复制和搜索时却要看作两个独立的符号。而在 PDF 中,合字的这些双重身份也正是通过 CMap 的「一对多」映射实现的。
合字
因此,复制文字重复的故障,其职责并不在于 PDF 的编码。「一对多」的映射并不是一种冗余或者区分,而是为了适应机械和用户的不同需要而必须加入的特定处理。说到底,还是预览 App 的优化功夫没下回家,没有意识到 PDF 文本在表明、复制、搜索时可能遭受不同的看待。这也随后应证了我之前文章中的一个观点:PDF 阅读器很多时候拼的不是功能有多丰富(反正都拼不过亲妹妹 Acrobat)pdf怎么不能复制文字,而是能不能做好复制、搜索这种基础系统的细节。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/ruanjian/article-119760-1.html
这个只有对的方向才是最好的不是吗
超期待哦