PDF复制文字PDF复制文字后出现「重字」是怎么一回事?

前两天,编辑 @⽂⼑漢三 在 Slack 上发给我一个 PDF 文件,问我知不知道为什么从里面复制出的中文会出现「重字」现象。他还提到,这个问题只在用系统自带的预览 app 打开时会出现,用其他 PDF 阅读器复制文字是正常的。 用预览 a
原标题:PDF复制文字后出现「重字」是怎么一回事?前两天,编辑@⽂⼑漢三在Slack上发给我一个PDF文件,问我知不知道为什么从里面复制出的中文会出现「重字」现象。他还提到,这个问题只在用系统自带的预览app打开时会出现,用其他PDF阅读器复制文字是正常的。用预览app复制出的「结巴」文字文刀拿这个问题来问我,恐怕是因为我之前写过一篇解释PDF格式的文章,觉得我大概会知道答案。不过他其实高估了我的知识水平——我刚开始也不知道这是怎么回事。不过,经过一番搜索,我最终初步搞清楚了问题成因。因为这个问题涉及到一些很有意思的细节,这里把探索的过程写出来,供有类似疑问的朋友参考。出现问题的PDF文档是由一篇少数派文章导出而得的。首先尝试复现问题:用预览app打开并随手复制一段,确实出现了很多重复的文字,看起来就像是「结巴」了一样,有一种莫名的喜感。换用PDFExpert打开再尝试复制,则没有这样的问题。虽然不知道问题的具体成因,但根据经验,文字复制中的故障往往与编码有关,而PDF格式正是编码问题的大户。我在之前的文章中提到,PDF格式是「不识字」的。在显示文字时,阅读器只是机械地根据PDF语句的指令,将字体资源中特定码位的字形绘制在坐标指定的位置,而并不关心自己画出来的到底是什么字。只有在进行复制、搜索等操作时,PDF才会根据内嵌的CMap,将内部字体的编码和Unicode编码对应起来。因此,如果CMap缺失或损坏,就无法从PDF中正常复制文字,但并不影响文档的外观。PDF中文本编码的原理这次的问题会不会也跟CMap有关呢?这就要查看PDF的源码才能知道。不过,大多数PDF都经过压缩,用文本编辑器直接打开是不可读的。为此,我们首先用qpdf将其解压:qpdf--stream-data=uncompresssspai.pdfsspai_unzip.pdf这样,就可以用任意文本编辑器打开查看代码了。但即使如此,PDF的源码结构也很混乱,从头翻看很难找到头绪。因此,我们可以从找准一个小处入手——例如标题中的这个「工」字。查询Unicode字符表,可以知道「工」字的编码是U+5DE5。既然PDF中有「工」字,那么源码中的某处一定会提到5de5这个编码。确实,简单搜索一下就可以找到这么一段:1beginbfcharendbfchar其中,beginbfchar和endbfchar正是CMap所用的语句。根据PDF语法,它表明PDF内嵌字体中编码为0ae1的字形,对应Unicode编码为U+2F2F和U+5DE5的两个字符。PDF源码中提及的「工」字Unicode编码奇怪了,为什么是两个字符?我们已经知道,U+5DE5就是汉字「工」,那这个多出来的U+2F2F又是什么?再次查询Unicode表,会发现U+2F2F竟然也是「工」。这两个字符是什么关系?它们是一回事吗?答案是否定的。仔细看一下两个字符的Unicode信息:U+5DE5的全名是「Ideographlabor,work;worker,laborerCJK」,位于CJKUnifiedIdeographs(中日韩统一表意文字)区块。显然,这就是我们日常所用的汉字「工」。另一方面,U+2F2F的全名是「KangxiRadicalWork」,其所在区块是「KangxiRadicals」(康熙字典部首)。换句话说,两个「工」虽然长得一模一样,但身份并不相同,一个是汉字,另一个是部首。这样一来,我们也就知道预览app是怎么复制出「重复」的文字了。实际上,它并没有复制出来两个一样的「工」字,而是同时复制出来了两个不同的字符——前一个是作为部首的「工」,后一个才是真正的汉字「工」。将预览的复制结果粘贴到文本转Unicode编码工具里,就能看得很明显:看似重复的文字实际上是两个不同的字符不过,PDF为什么要在CMap中作这样「一对多」的映射呢?的确,从预览app的角度看,它或许还有理由感到一点小委屈:我做错了什么,PDF明明告诉我这是两个字啊?回答这个问题,就要先了解「康熙字典部首」区块中的字符的用途。对此,这个网页解释得很清楚(原文为繁体中文;粗体为笔者所加):既然全部部首的字元都在另一个地方编了码,那么为何要再次编码呢?据统一码(即Unicode——笔者注)的文件指出,本区块的字元只作部首之用,不应该当作一般文字用途,文件更进一步提出,必要时甚至可以用不同的字型格式,表明是属于本区块的字元。换句话说,例如编辑一本字典,部首页、部首标题和「参见某部若干画」等文字,都应使用本区块内的字元;而内文和字头、词条等文字部分,则应使用「中日韩统一表意文字区块」中的字元。这样做的原意,是希望让机器知道该字元现时所充当的角色:是「一般文字」,还是「部首文字」。当然,这些分别对人类来说可能没有作用,但对机器的语意分析是十分重要的。实际上,类似的需求在使用拉丁字符的语言中同样存在。最典型的是西文排版中所谓「合字」(ligature)的概念,即针对ff、fi这样笔画容易「打架」的字符组合,将其当作一个整体来专门设计造型。因此,这些字符组合在Unicode中需要有独立的码位,如ff(U+FB00)、fi(U+FB01)等(请试着动手复制一下这些字符)。可是,合字在显示时是一个整体,而在复制和搜索时却要看作两个独立的字符。而在PDF中,合字的这种双重身份也正是通过CMap的「一对多」映射实现的。合字因此,复制文字重复的故障,其责任并不在于PDF的编码。「一对多」的映射并不是一种冗余或者混淆,而是为了适应机器和用户的不同需要而必须加入的特殊处理。说到底,还是预览App的优化功夫没下到家,没有意识到PDF文本在显示、复制、搜索时应该受到不同的对待。这也再次应证了我之前文章中的一个观点:PDF阅读器很多时候拼的不是功能有多丰富(反正都拼不过亲儿子Acrobat),而是能不能做好复制、搜索这些基础功能的细节。客户端、关注少数派公众号,让你的工作更有效率⏱少数派PiStore商店

本文来自投稿,不代表长河网立场,转载请注明出处: http://www.changhe99.com/a/za6ln0LDwm.html

(0)

相关推荐