如何使用 java 读取 pdf 中的控制字符

How To read control characters in a pdf using java

我正在使用 PDFBox 阅读 PDF 文件。但是有些字符打印不好,像控制字符一样打印。有人帮助从控制字符中读取值。我附上了图片请看一下那张图片 示例 PDF:

截图:

示例代码

class PDFManager {

   private PDFParser parser;
   private PDFTextStripper pdfStripper;
   private PDDocument pdDoc ;
   private COSDocument cosDoc ;

   private String Text ;
   private String filePath;
   private File file;

   public PDFManager() {

   }

   public String ToText() throws IOException {
       this.pdfStripper = null;
       this.pdDoc = null;
       this.cosDoc = null;
       file = new File(filePath);
       parser = new PDFParser(new FileInputStream(file));

       parser.parse();
       cosDoc = parser.getDocument();
       pdfStripper = new PDFTextStripper(); 
       pdDoc = new PDDocument(cosDoc);  
       pdDoc.getNumberOfPages();
       pdfStripper.setStartPage(3);
       pdfStripper.setEndPage(4); 
       Text = pdfStripper.getText(pdDoc);

       return Text;
   }

   public void setFilePath(String filePath) {
       this.filePath = filePath;
   }
}

你得到正确的泰米尔字母和不正确的控制序列的原因是各自的字体

  • 没有 ToUnicode 映射和
  • 有一个 编码 条目使用 non-standard 一些字形的名称。

在这种情况下,PDFBox 无法在没有帮助的情况下正确提取相关字符。

为了帮助 PDFBox,您必须检查每个 non-standard 名称的所有文档(或至少在足够大的子集中)绘制的字形是否相同。如果是这种情况,您可以告诉 PDFBox 添加从每个名称到相应绘制字母的 Unicode 值的映射到其已知字形映射库。

更详细:

问题

我将在这里用一个例子来说明这个问题。

在 OP 提到的第 3 页上,第一个文本是使用等同于这些的指令绘制的:

/R9 8.04 Tf
0.999418 0 0 1 519.6 791.721 Tm
[<01>6.75242<0C>-0.371893<0D>4.89295<07>3.77727<14>-6.13989<35>-4.51376<02>-5.00233<0F>187.988]TJ 

(我只是将字符串的表示形式更改为十六进制,因为各个代码大多在控制字符范围内,因此不会在此处正确显示。)

本页字体R9没有ToUnicode映射。也没有任何 ActualText 条目。因此,PDFBox 只能使用字体的 Encoding 条目:

<<
  /BaseEncoding/WinAnsiEncoding
  /Differences[1
    /u0BC6/u0B9A/g125/u0BC8/u0BA9/g121/u0B9F/u0BAE
    /u0BB1/g123/space/u0BA4/u0BBE/g148/u0BBF/u0B8E
    /g122/u0BAA/u0BAF/g129/g130/g178/g127/u0B92
    /g162/g116/u0B95/u0BC0/g158/u0BA8/u0BB2/colon
    /u0B85/g117/g173/g132/u0BB3/g182/g142/one
    /period/g175/u0BB5/u0BB0/g126/u0B86/u0BC7/g186
    /g156/g131/g143/two/g118/g133/g190/hyphen
    /zero/five/g171/g120/g146/g169/g152/parenleft
    /seven/parenright/three/g180/u0BA3/eight/g136/u0BB4
    /u0B9C/four/six/g124/nine/g135/slash/g172
    /comma/u0B87/numbersign/g128/g147/g160/u0B9E/u0B89
    /u0BB7/g119/g157/g167/g191/g188/g170/g145
    /g181/u0BB8/u0B90/uni25CC/u0BCD/u0BB9/u0BC1/u0B88
    /g163/u0BD7/g184/u0B8F/g174/g153/g138/g185
    /g134/g149/g176]
  /Type/Encoding
>> 

如您所见,它首先声明了一个基本编码 WinAnsiEncoding 可以忽略它,因为字体使用的代码范围内的所有映射或多或少都被替换为差异 数组。

Differences数组中你可以找到

  • 一些标准名称,例如 commatwo;
  • 许多使用 uXXXX 方案表示 unicode 代码点的名称,例如 u0BC6u0B9A;
  • 一个名称代表使用uniXXXX方案的unicode代码点:uni25CC;和
  • 许多完全 non-standard 使用 gXXX 方案的名称,例如 g121g176.

PDFBox 支持标准名称(很明显)并且另外都使用 unicode 代码点命名变体(经常发现并且其解释非常直接)。

它不支持开箱即用的其他名称。

因此,为第一个文本绘制指令提取的文本是:

<01> - /u0BC6 - 0BC6 - ெ
<0C> - /u0BA4 - 0BA4 - த
<0D> - /u0BBE - 0BBE - ா
<07> - /u0B9F - 0B9F - ட
<14> - /g129 ?? 0014 - <DEVICE CONTROL FOUR>
<35> - /g118 ?? 0035 - 5
<02> - /u0B9A - 0B9A - ச
<0F> - /u0BBF - 0BBF - ி

导致提取文本的第一行:

顺便说一下,这对应于实际 PDF 中的这一部分:

允许正确提取的可能方法

PDFBox 提供允许您将名称添加到其已知名称映射的机制。如果这些 gXXX 名称在您的文档中经常代表相同的相应字符,因此,您可以调整 PDFBox 文本提取以满足您的要求。

stable PDFBox 版本 1.8.X 使用与 2.0.0 候选版本不同的机制。因此:

对于 PDFBox 1.8.X 您必须创建一个字形列表文本文件。对于每个字形,它包含一行和 2 个 semicolon-delimited 字段、字形名称和 Unicode 标量值,例如

A;0041
AE;00C6

然后你定义一个系统 属性 glyphlist_ext 指向那个列表,例如启动程序时

java -Dglyphlist_ext=/path/to/my/extra/glyphs ...

对于PDFBox 2.0.0这个机制已经被多次替换和移动,我不知道现在是哪个。

PDFBOX-2379 上工作时,如果发现上述系统 属性,将抛出一个异常:

throw new UnsupportedOperationException("glyphlist_ext is no longer supported, "
    + "use GlyphList.DEFAULT.addGlyphs(Properties) instead");

很遗憾 GlyphList 没有那个方法 addGlyphs 了。

在处理 PDFBOX-2380 时,它已被删除并替换:

I've replaced the static DEFAULT glyph list with a getAdobeGlyphList() method, as some PDFBox font internals require this to be the AGL and not some other additional glyph list. The loading and use of the additional glyphlist is application specific and so has been moved to PDFStreamEngine, where the getGlyphList() method can be overridden to pass custom glyph lists to fonts.

不幸的是,PDFStreamEngine 没有那个 getGlyphList 方法了。

我目前没有心情继续四处寻找以再次找到该功能。精氨酸

making-of

OP 在评论中询问我如何从相关 PDF 文件中检索上述信息。

首先,我使用了一个 PDF 内部浏览应用程序,例如iText RUPS or PDFBox PDFDebugger, to inspect the PDF and the PDF specification ISO 32000-1 了解我正在检查的内容。

OP 特别指出了他文档的第 3 页,因此我在内容流(参见 ISO 32000-1 部分)中查找了第一个显示运算符的文本(参见 ISO 32000-1 第 9.4.3 节)该页面的 7.8.2)(参见 ISO 32000-1 第 7.7.3.3 节):

这些几乎就是我上面引用的说明。但是,如您所见,不幸的是,这里无法检查字符串,因为它们的内容大部分在 Unicode 控制字符范围内。因此,我保存了内容流(right-click,上下文菜单)并检查了这些指令的十六进制视图

使用se 信息我在上面创建了指令引用。

这些说明中选择的字体(参见 ISO 32000-1 第 9.6 节)是 R9(参见 ISO 32000-1 第 9.3.1 节),所以我继续查看第 3 页上具有该名称的字体资源(参见 ISO 32000-1 第 7.8.3 节),首先搜索 ToUnicode 条目未成功(参见 ISO 32000-1 第 9.10 节。 3), 然后成功找到一个 Encoding (cf. ISO 32000-1 section 9.6.6):

我复制了这个 prettied-up 以获得上面的编码引用。

根据这些信息,我手动创建了 table,其中包含字形 ID(来自指令块中显示操作的文本)、相应的名称(来自编码差异)、假定的 unicode 代码点(派生来自 uXXXX 名称,gXXX 再次命名字形 ID)和字符(来自许多 Unicode table 站点之一互联网)。

为了从实际的 PDF 页面中找到相应的部分,我采用了 Tm 文本矩阵设置操作的最后两个参数(参见 ISO 32000-1 第 9.4.2 节)考虑聚合转换矩阵变化(参见 ISO 32000-1 第 8.4 节)。这些是下面的文字显示指令绘制的文字基线的起点坐标。