8

我正在尝试使用此方法修改 pdf(第一个代码块 - 使用 PDFStreamParser 并遍历 PDFOperator,然后在需要时更新 COSString):

http://www.coderanch.com/t/556009/open-source/PdfBox-Replace-String-double-pdf

我遇到了一些 UTF-8 字符(变音符号)的问题:当我打印要更新的文本时,它会显示为“Societ??ii Na?ionale”(其中“?”是 0002 或 0004 之类的代码)。

有趣的是:

  1. 当我编写更新的 pdf 文件时,字符显示正确(即使我无法检测到并替换它们)
  2. 如果我尝试使用 PDFTextStripper 的 getText(...) 剥离文本,则文本被完美提取。
  3. 我尝试了 2 个 pdfbox 版本:1.5.0(其行为如上所述)和 1.8.1(最终的书面 pdf 文件未正确显示特殊字符并且文档中出现“空”字符串)

对于用于更新 pdf 的类(或至少尝试...),我可以做什么(配置)以便正确显示所有 UTF-8 字符?

编辑:

截屏:在此处输入图像描述

编辑2:

我搜索了 PDFTextStripper 及其超类中的 pdfbox 源代码,发现了文本是如何提取的:

在 processStream 方法的开头,我们有

graphicsState = new PDGraphicsState(aPage.findCropBox());

当剥离 processEncodedText 中的文本时,PDFont 类的实例使用如下:

final PDFont font = graphicsState.getTextState().getFont();

并且文本是从 byte[] 中提取的:

String c = font.encode( string, i, codeLength );

新问题是,当我用相同的 2 行代码实例化 PDFont 类时,我得到一个“null”字体类,因此我不能使用 .encode(...) 方法。这些类的源代码在这里: http: //grepcode.com/file/repo1.maven.org/maven2/org.apache.pdfbox/pdfbox/1.5.0/org/apache/pdfbox/util/PDFStreamEngine.javahttp://grepcode.com/file/repo1.maven.org/maven2/org.apache.pdfbox/pdfbox/1.5.0/org/apache/pdfbox/util/PDFTextStripper.java

我现在正在挖掘更多...

4

2 回答 2

20

您不能只替换字符串中的文本。我不是随便说的。很多年前我在Acrobat上工作过,并且在最初的版本中做过文本搜索工具,所以我对文本编码的问题有相当深入的了解。主要问题是 PDF 中的每个字符串都以某种方式编码。这是因为 PDF 是在 Unicode 普遍可用之前制作的,并且在 PostScript 中有一段历史。PosctScript 喜欢为字体提供非常灵活的编码方法,并鼓励重新编码。

因此,让我们退后一步,了解整个情况。

默认情况下,PDF 中的字符串中要使用文本运算符显示的字符被编码为一系列 8 位字符。为了确定为每个字节绘制的字形,该字节被推入该字体的编码向量。编码向量将字节映射到字形名称,然后在字体中查找并在页面上绘制。请注意,此描述是半真半假(稍后会详细介绍)。

大多数生成 PDF 的应用程序都很友好,并且只使用标准编码,例如StandardEncodingor WinAnsiEncoding,其中大多数都非常合理。其他人将使用标准编码以及编码增量,这是标准编码与编码内容的区别。

一些应用程序试图在他们生成的 PDF 中更加节俭,因此他们查看他们使用的字形并决定嵌入字体的子集。如果他们只使用大写和小写罗马字母和数字,他们会在没有这些元素的情况下重建字体,并且可能会选择重新索引它们并提供一个编码向量,使得字节 0x00 进入字形“a”,而 0x01 进入字形“b”等等。

现在回到半真半假。有一类字体由字符 ID(或 CID)编码,TrueType 和 OpenType 字体属于该类别。在这种情况下,您可以访问 Unicode,但是再次有一个编码步骤,您可以将现在为 UTF16BE 的字符串映射到用于从字体中获取字形的 CID。并且没有特别好的理由,Adobe 使用 PostScript 函数来进行映射。再一次,这大约是 3/4 的事实,因为对于旧的中文、日文和韩文字体管理也有不同的编码。

因此,在您轻松地将字符放入 PDF 字体的字符串之前,您必须问几个问题:

  1. 我的字形在字体中吗?
  2. 我的字形在编码中吗?
  3. 我的字形的编码是什么?

其中任何一个都可能与您的期望不同。因此,例如,如果您想输入 Ä(一个 diresis),您必须查看该字体是否具有它的字形(可能不存在,因为该字体是一个子集)。那么字体可能有一个有趣的编码,可能不包括字形。最后,用于 Ä 的实际字节值可能不是标准的。

因此,当我看到有人试图简单地替换 PDF 内容中的一大段文本时,我所看到的只是一个痛苦的世界。对于大多数理智的 PDF 来说,这在 90% 的情况下都有效,但对于任何异国情调的东西——祝你好运。PDF 的文本渲染怪癖非常痛苦,有时更容易将其视为只写格式。

于 2013-04-12T14:12:44.320 回答
1

最后,似乎提取 pdf 文件中的字体的过程相当复杂。我无法明确使用字体,所以我在 PDFStreamEngine 的代码和扩展 OperatorProcessor 的类中进行了搜索,发现 PDFont 对象是如何创建到地图中的(我几乎复制了提取变音符号所需的代码块) . 因此,之后我在通过 parser.getTokens() 交互时使用检测到的字体对“字符串”中的每个字符调用 encode(...) 方法。

于 2013-04-16T08:29:40.140 回答