Java PDFBox - 读取和修改带有特殊字符的pdf(变音符号)

时间:2013-04-12 06:38:02

标签: java pdf utf-8 diacritics pdfbox

我正在尝试使用此方法修改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文件没有正确显示特殊字符,文档中出现“null”字符串)
  4. 我可以为用于更新pdf的类(或至少尝试...)做什么(配置),以便正确显示所有UTF-8字符?

    编辑:

    屏幕截图:enter image description here

    编辑2:

    我搜索了PDFTextStripper及其超类中的pdfbox源代码,然后我发现了文本的提取方式:

    在processStream方法的开头我们有

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

    在processEncodedText中剥离文本时,像这样使用PDFont类的实例:

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

    ,文本从字节[]中提取:

    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

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

2 个答案:

答案 0 :(得分:19)

您不能只替换字符串中的文本。我不轻易说。我多年前曾在Acrobat上工作,并在初始版本中使用了文本搜索工具,因此我对文本编码问题有了相当深刻的理解。主要问题是PDF中的每个字符串都以某种方式编码。这是因为PDF是在Unicode通常可用之前制作的,并且在PostScript中有历史记录。 PosctScript喜欢使用非常灵活的字体编码方法,并鼓励重新编码。

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

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

生成PDF的大多数应用都很友好,只使用标准编码,例如StandardEncodingWinAnsiEncoding,其中大多数都非常合理。其他人将使用标准编码以及编码增量,这是从标准编码到编码的差异。

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

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

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

  1. 我的字形是否为字体?
  2. 我的字形是否在编码中?
  3. 我的字形的编码是什么?
  4. 其中任何一个都可能与您的期望不同。因此,例如,如果你想放入Ä(一个方向),你必须看看字体是否有它的字形(可能不存在,因为字体是一个子集)。然后字体可能有一个有趣的编码,可能不包括字形。最后,用于Ä的实际字节值可能不是标准值。

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

答案 1 :(得分:1)

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