首页 > 解决方案 > 通过 pdfbox 从 pdf 文档中提取文本时没有 Unicode 映射错误,因为字体字典中缺少 ToUnicode CMap 条目

问题描述

Adobe Acrobat Pro“内容视图”显示字符正常,但当我复制和粘贴时,它们无效。但如果“复制并格式化”则正常。坏案例图片

比如第一个字母“重”,<a href="https://github.com/zhongguogu/PDFBOX/blob/master/pdf/1.pdf" rel="nofollow noreferrer">bad case pdf file when I use 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() 时,它返回 null。

@mkl 如果有办法解决这个问题。在此先感谢。

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}"

标签: javapdfbox

解决方案


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

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

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

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

因此,Adobe Acrobat 复制和粘贴(它使用前面提到的算法和ActualText分析的组合)无法提取该中文文本。

因此,Adobe Acrobat Pro 中的“带格式复制”显然必须使用一些超出 PDF 规范提出的机制的信息。

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

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

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

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

我们的团队已经调查了这个问题,我想与您分享您用于创建示例 PDF 文件的软件使用了 PDFXC30 字符集。这个字符集合不是标准的,我们没有关于这个编码的任何信息。这使得目前无法正确提取文本。

(shahzadlatif 在PdfExtractor 编码问题线程中的最新响应)

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


推荐阅读