【问题标题】:Java - PDFBox - ReplaceString - Issues with parsed tokens (possibly encoding?)Java - PDFBox - ReplaceString - 解析令牌的问题(可能是编码?)
【发布时间】:2016-01-12 09:17:55
【问题描述】:

我一直在努力解决与 PDFBox 和 PDF 编辑相关的问题。我被分配了编辑给定 PDF 文件的几个字符串的任务,并将已编辑字符串的文件的镜像版本输出到其中。有人告诉我,过去使用这个工具已经解决了这个问题,所以有人告诉我也这样做。我正在使用的功能是这样的:

  public void doIt( String inputFile, String outputFile, String strToFind, String message)
    throws IOException, COSVisitorException
{
    // the document
    PDDocument doc = null;
    try
    {
        doc = PDDocument.load( inputFile );
        List pages = doc.getDocumentCatalog().getAllPages();
        for( int i=0; i<pages.size(); i++ )
        {
            PDPage page = (PDPage)pages.get( i );
            PDStream contents = page.getContents();
            PDFStreamParser parser = new PDFStreamParser(contents.getStream() );
            parser.parse();
            List tokens = parser.getTokens();
            for( int j=0; j<tokens.size(); j++ )
            {
                Object next = tokens.get( j );
                if( next instanceof PDFOperator )
                {
                    PDFOperator op = (PDFOperator)next;
                    //Tj and TJ are the two operators that display
                    //strings in a PDF
                    if( op.getOperation().equals( "Tj" ) )
                    {
                        //Tj takes one operator and that is the string
                        //to display so lets update that operator
                        COSString previous = (COSString)tokens.get( j-1 );
                        String string = previous.getString();
                        string = string.replaceFirst( strToFind, message );
                        previous.reset();
                        previous.append( string.getBytes("ISO-8859-1") );
                    }
                    else if( op.getOperation().equals( "TJ" ) )
                    {
                        COSArray previous = (COSArray)tokens.get( j-1 );
                        for( int k=0; k<previous.size(); k++ )
                        {
                            Object arrElement = previous.getObject( k );
                            if( arrElement instanceof COSString )
                            {
                                COSString cosString = (COSString)arrElement;
                                String string = cosString.getString();
                                string = string.replaceFirst( strToFind, message );
                                cosString.reset();
                                cosString.append( string.getBytes("ISO-8859-1") );
                            }
                        }
                    }
                }
            }
            //now that the tokens are updated we will replace the
            //page content stream.
            PDStream updatedStream = new PDStream(doc);
            OutputStream out = updatedStream.createOutputStream();
            ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
            tokenWriter.writeTokens( tokens );
            page.setContents( updatedStream );
        }
        doc.save( outputFile );
    }
    finally
    {
        if( doc != null )
        {
            doc.close();
        }
    }
}

PDFBox 示例 (https://svn.apache.org/repos/asf/pdfbox/tags/1.5.0/pdfbox/src/main/java/org/apache/pdfbox/examples/pdmodel/ReplaceString.java) 中包含的文件中使用的代码是什么。

然而,我得到的文件根本没有被这个函数修改。什么都没有发生。经过进一步检查,我决定分析解析器生成的标记的顺序。除了 COSString 元素之外,该文件的所有内容都被正确解析,这些元素包含看起来像是被错误编码的乱码(一堆随机符号和数字)。我尝试解析其他文档,并且该函数适用于其中一些文档,但不适用于我作为输入传递的所有内容(乳胶输出文件已正确修改并已正确编码 COSStrings,而其他自动生成的 pdf 没有产生带有乱码 COSString 内容的结果) .我也相当确定结构的其余部分被正确读取,因为我在不同的文件上重建输出,并且输出文件看起来与输入完全相同,这似乎意味着文件结构正在被正确分析。该文件包含 Identity-H 编码的字体。

我尝试使用 PDFTextStripper(从 PDF 中提取文本)解析相同的文件,然后从那里解析输出返回正确的文本输出,使用以下方法:

 PDFTextStripper pdfStripper = new PDFTextStripper("UTF-8");
            String result = pdfStripper.getText(doc);
            System.out.println(result); 

可能是编码问题?我可以告诉 PDFStreamParser(或负责的人)在读取时强制编码吗?由于文本提取工作正常,这甚至是编码问题吗?

提前感谢您的帮助。

【问题讨论】:

    标签: java parsing pdf encoding pdfbox


    【解决方案1】:

    某些文件使用字体子集。假设子集仅使用字符 E、G、L 和 O。因此 GOOGLE 将在文件中显示为十六进制字节值 2、4、4、2、3 和 1。

    现在如果你想把 GOOGLE 改成 APPLE 你会遇到三个问题:

    1) 您的子集不包含字符 A、L 和 P

    2) 大小会不同

    3) 您正在搜索的字符串很可能被分成几个部分。

    顺便说一句,当前版本是 1.8.10。 ReplaceString 实用程序已在即将发布的 2.0 版本中删除,以避免给人一种可以轻松替换字符的错觉。

    这个答案有点推测,因为您没有链接到 PDF。

    【讨论】:

    • 你的回答给了我一个很好的工作线索。原来我正在处理的 PDF 没有使用标准 Tj 标签来输入文本,而是使用了 PDXObject 字典,其中包含插入字符串的映射并将它们编码为外部对象。感谢您的回答!
    【解决方案2】:

    内部PDF文本可以存储在两个地方:

    1. 内容流

    2. 资源中的 X 对象

    在内容流中,大部分文本都与 TJ 或 Tj 运算符相关联。但是与 Tj 或 TJ 相关的文本并不总是 ASCII 格式,它可能是一些字节值。在使用适当的编码和映射将字符代码映射到 unicode 值之后,我们可以从这些字节值中提取文本。在提取文本时,我们使用映射和编码,但我们没有反向映射来检查字形是否属于哪个字符代码。所以基本上我们应该将要替换的字符串的字符代码替换为新字符串的字符代码。

    例子:

    1. (Text) Tj
    2. (12 45 5 3)Tj
    

    我们还应该替换内容流中的字符串以及资源中的 X 对象(如果存在)。

    所以我认为这可能会有所帮助。

    祝你好运!

    【讨论】:

    • “内部 PDF 文本可以存储在两个位置:” - 一些 PDF 处理器(试图使文本不可提取)将其放在第三个位置:带有用于填充页面的页面大小的图块。
    猜你喜欢
    • 2023-03-12
    • 2011-05-13
    • 1970-01-01
    • 2016-03-14
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    • 1970-01-01
    相关资源
    最近更新 更多