【问题标题】:Java PDFBox - Reading and modifying a pdf with special characters (diacritics)Java PDFBox - 使用特殊字符(变音符号)读取和修改 pdf
【发布时间】:2013-04-04 14:06:38
【问题描述】:

我正在尝试使用此方法修改 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

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

【问题讨论】:

  • 让我们一次拿一个。因此,当您打印包含一些变音符号的文本时,它会显示为 ???还是一些数字?您能否提供一个打印语句的示例以及一些输出?
  • 您找到的字符串替换代码here on coderanch 几乎不起作用: 1. PDF 页面内容中字符串的编码完全取决于问题的字体;您的代码采用标准编码。 2. 单个单词的字母(或当前情况下的占位符)可能会在内容流中传播到多个字符串;您的代码不会找到这样的分隔占位符。 3. 字体只能部分嵌入PDF;如果您的替换字符串使用未使用的字符,它们将不会显示。 ...
  • @Eugene :文本显示正确,但变音符号显示为 4 位数的正方形(顶部两个 - 例如 00,底部两个,例如 02)。我无法将输出剪切/粘贴到浏览器中(因为它们将显示为空格),但我将在 Eclipse 控制台中附上带有输出的屏幕截图。
  • @ice13ill 我问的原因是因为我有一个非常相似的事情要做(几乎)阅读和解析包含罗马尼亚字符(也有变音符号)的 pdf。我使用 itext 而不是 pdfbox,效果很好

标签: java pdf utf-8 diacritics pdfbox


【解决方案1】:

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

所以让我们退后一步,了解整个情况。

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

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

一些应用试图在生成的 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 的文本渲染怪癖非常痛苦,有时更容易将其视为只写格式。

【讨论】:

  • 感谢您的回答。我还用一些关于 pdfbox 中代码行的发现更新了我的问题,这些发现与从我的 pdf 文件中正确提取文本的类有关。
  • @plinth 这个答案很可能不会得到太多的支持,但对我来说它应该得到一批金牌。
【解决方案2】:

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

【讨论】:

  • 你可以通过哪种方式获取字体并对字符串进行编码?我正在处理中文字符串并遇到了同样的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
  • 2011-10-29
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
相关资源
最近更新 更多