java - 通过 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}"
解决方案
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 使用 Adobe-GB1、Adobe-CNS1、Adobe-Japan1 或 Adobe-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 中,以便为像您这样的文件启用文本提取。
推荐阅读
- cordova - 科尔多瓦中的 inappbrowser 未从弹出窗口返回 3ds 付款数据
- charts - Plotly 错误消息错误:客户端错误:(413)请求实体太大
- swift - Swift:使嵌入在导航控制器中的 UITableViewController 的背景图片填满整个屏幕
- database - 用于存储 IoT 数据的 DynamoDB 地图与列表
- java - android studio改造将PDF上传到服务器失败
- excel - Excel MySQL - 多台计算机
- python - 如何使用 Selenium Python 迭代谷歌页面
- eclipse - 在 Eclipse 源视图编辑器中实现标记?
- java - 如何仅在几个页面而不是整个应用程序中禁用电容器插件中的屏幕截图
- reactjs - 使用 MaterialUI 和 Typescript 在 React 中传递给 onKeyPressed 函数的事件的正确类型是什么?