由于字体字典中缺少 ToUnicode CMap 条目,pdfbox 从 pdf 文档中提取文本时没有 Unicode 映射错误

No Unicode mapping error when extract texts from pdf document by pdfbox for missing ToUnicode CMap entry in font dict

Adobe Acrobat Pro“Content View”显示字符正常,但是我复制粘贴的时候是invalid.but如果“copy with formating”就正常了bad case image

例如首字母"重",bad case pdf file 当我使用pdfbox提取字母时,一些警告提示。

一月 08, 2021 11:14:37 上午 org.apache.pdfbox.pdmodel.font.PDType0Font toUnicode
警告: No Unicode mapping for CID+18429 (18429) in font GVAQVQ+SimSun

PDFont.loadUnicodeCmap() 因为字体GVAQVQ+SimSun中没有ToUnicode CMap入口,所以PDType0Font.toUnicodeCMap为空。所以当调用 PDFont.toUnicode() 时,它 return null.

@mkl 如果有什么方法可以提前解决这个problem.Thanks。

PDType0Font/null, PostScript name: GVAQVQ+SimSun

   0 = {SmallMap$SmallMapEntry@2123} "COSName{BaseFont}" -> "COSName{GVAQVQ+SimSun}"
   1 = {SmallMap$SmallMapEntry@2124} "COSName{DescendantFonts}" -> "COSArray{[COSDictionary{COSName{BaseFont}:COSName{GVAQVQ+SimSun};COSName{CIDSystemInfo}:COSDictionary{COSName{Ordering}:COSString{Identity};COSName{Registry}:COSString{PDFXC30};COSName{Supplement}:COSInt{0};};COSName{DW}:COSInt{1000};COSName{FontDescriptor}:COSObject{COSDictionary{COSName{Ascent}:COSInt{859};COSName{AvgWidth}:COSInt{500};COSName{CapHeight}:COSInt{668};COSName{Descent}:COSInt{-141};COSName{Flags}:COSInt{32};COSName{FontBBox}:COSArray{COSInt{-8};COSInt{-145};1000;859;};COSName{FontFile2}:COSObject{COSDictionary{COSName{Length}:COSInt{175201};COSName{Filter}:COSArray{COSName{FlateDecode};};COSName{Length1}:COSInt{468544};}COSStream{-708342007}};COSName{FontName}:-120083354;COSName{ItalicAngle}:0;COSName{Leading}:COSInt{141};COSName{MaxWidth}:1000;COSName{MissingWidth}:500;COSName{StemH}:COSInt{70};COSName{StemV}:70;COSName{Type}:COSName{FontDescriptor};COSName{XHeight}:COSInt{438};}};COSName{Subtype}:COSName{CIDFontType2};COSName{Type}:COSNa
   2 = {SmallMap$SmallMapEntry@2125} "COSName{Encoding}" -> "COSName{Identity-H}"
   3 = {SmallMap$SmallMapEntry@2126} "COSName{Subtype}" -> "COSName{Type0}"
   4 = {SmallMap$SmallMapEntry@2127} "COSName{Type}" -> "COSName{Font}"

"COSName{FontDescriptor}" -> "COSObject{15, 0}"
key = {COSName@2168} "COSName{FontDescriptor}"
value = {COSObject@2169} "COSObject{15, 0}"
baseObject = {COSDictionary@2209} "COSDictionary{COSName{Ascent}:COSInt{859};COSName{AvgWidth}:COSInt{500};COSName{CapHeight}:COSInt{668};COSName{Descent}:COSInt{-141};COSName{Flags}:COSInt{32};COSName{FontBBox}:COSArray{COSInt{-8};COSInt{-145};COSInt{1000};859;};COSName{FontFile2}:COSObject{COSDictionary{COSName{Length}:COSInt{175201};COSName{Filter}:COSArray{COSName{FlateDecode};};COSName{Length1}:COSInt{468544};}COSStream{-708342007}};COSName{FontName}:COSName{GVAQVQ+SimSun};COSName{ItalicAngle}:COSInt{0};COSName{Leading}:COSInt{141};COSName{MaxWidth}:1000;COSName{MissingWidth}:500;COSName{StemH}:COSInt{70};COSName{StemV}:70;COSName{Type}:COSName{FontDescriptor};COSName{XHeight}:COSInt{438};}"
needToBeUpdated = false
items = {SmallMap@2211}  size = 16
0 = {SmallMap$SmallMapEntry@2214} "COSName{Ascent}" -> "COSInt{859}"
1 = {SmallMap$SmallMapEntry@2215} "COSName{AvgWidth}" -> "COSInt{500}"
2 = {SmallMap$SmallMapEntry@2216} "COSName{CapHeight}" -> "COSInt{668}"
3 = {SmallMap$SmallMapEntry@2217} "COSName{Descent}" -> "COSInt{-141}"
4 = {SmallMap$SmallMapEntry@2218} "COSName{Flags}" -> "COSInt{32}"
5 = {SmallMap$SmallMapEntry@2219} "COSName{FontBBox}" -> "COSArray{[COSInt{-8}, COSInt{-145}, COSInt{1000}, COSInt{859}]}"
6 = {SmallMap$SmallMapEntry@2220} "COSName{FontFile2}" -> "COSObject{12, 0}"
7 = {SmallMap$SmallMapEntry@2221} "COSName{FontName}" -> "COSName{GVAQVQ+SimSun}"
8 = {SmallMap$SmallMapEntry@2222} "COSName{ItalicAngle}" -> "COSInt{0}"
9 = {SmallMap$SmallMapEntry@2223} "COSName{Leading}" -> "COSInt{141}"
10 = {SmallMap$SmallMapEntry@2224} "COSName{MaxWidth}" -> "COSInt{1000}"
11 = {SmallMap$SmallMapEntry@2225} "COSName{MissingWidth}" -> "COSInt{500}"
12 = {SmallMap$SmallMapEntry@2226} "COSName{StemH}" -> "COSInt{70}"
13 = {SmallMap$SmallMapEntry@2227} "COSName{StemV}" -> "COSInt{70}"
14 = {SmallMap$SmallMapEntry@2228} "COSName{Type}" -> "COSName{FontDescriptor}"
15 = {SmallMap$SmallMapEntry@2229} "COSName{XHeight}" -> "COSInt{438}"

PDFBox 文本提取根据 PDF 规范 ISO 32000-1 第 9.10.2 节“将字符代码映射到 Unicode 值”中介绍的算法进行工作。尝试将此算法应用于您的文件时,无法提取使用 SimSun 字体嵌入子集 (F2) 绘制的文本:

  • "如果字体字典包含一个ToUnicode CMap" - F2没有一个 ToUnicode CMap.
  • "如果字体是简单字体" - F2不是简单字体
  • “如果字体是复合字体” - F2确实是复合字体,但是...
    • ”使用 Table 118 中列出的预定义 CMap 之一(Identity–H 和 Identity–V 除外)” - F2 使用 Identity-H.
    • "或其后代 CIDFont 使用 Adob​​e-GB1、Adobe-CNS1、Adobe-Japan1 或 Adob​​e-Korea1 字符集" - F2 使用 PDFXC30-Identity 字符集。
  • 如果这些方法无法生成 Unicode 值,则无法确定字符代码代表什么,在这种情况下,符合要求的 reader 可以选择他们选择的字符代码。

因此,PDFBox 中实现的文本提取无法提取中文文本。

PDF 规范中提供的文本提取期间文本信息的替代来源是结构元素或标记内容序列的 ActualText 条目。但是您的 PDF 也没有任何此类 ActualText 条目。

因此,Adobe Acrobat copy&paste(使用前面提到的算法和 ActualText 分析的组合)无法提取该中文文本。

因此 Adob​​e Acrobat Pro 中的“带格式复制”显然必须使用一些超出 PDF 规范建议的机制的信息。

检查嵌入的字体资源本身 可以看到它既不包含自己的 Unicode 映射,也不包含任何标准名称。但值得注意的是,字形编号不是连续编号的,而是有间隙的。可能这些数字在子集化过程中已从完整字体中保留下来。

因此,Adobe Acrobat Pro 在“复制并格式化”您的中文文本期间似乎会执行以下任一选项:

  • 他们知道 PDFXC30-Identity 字符集合的详细信息,无论是正式来自 PDF-XChange 还是通过逆向工程,并使用该信息进行提取。
  • (如果在子集化过程中从完整字体中保留了字形编号的假设是正确的:)他们知道 SimSun 字体并且有一个字形编号到 Unicode 映射以用于提取。
  • 他们获取 SimSun 字体的完整副本(内部提供或由主机提供 OS),将其中的字形与嵌入子集中的字形进行比较,并从文本中导出到 Unicode 的映射提取.
  • 他们将 OCR 应用于嵌入字体的各个字形,并从结果中导出到 Unicode 的映射。

谷歌搜索 PDFXC30-Identity 字符集,发现有许多文本提取工具存在问题,例如在 Aspose 论坛上,您可以阅读:

Our team has looked into this issue and I would like to share with you that the software you used to create the sample PDF files used PDFXC30 character collection. This character collection is not standard and we don’t have any information about this encoding. This makes correct text extraction impossible at the moment.

(shahzadlatif 在 PdfExtractor encoding issue 线程中的最新回复)

如果您可以从可信赖的来源提供 PDFXC30 字符集映射文件,PDFBox 开发人员可能会将它们包含到 PDFBox 中,以便为像您这样的文件启用文本提取。