首页 > 解决方案 > 使用PDFbox在pdf文件中用unicode文本替换字符串?

问题描述

我需要从 PDF 文件中读取字符串并将其替换为 Unicode 文本。如果是 ASCII 字符,一切都很好。但是对于 Unicode 字符,它会显示问号/垃圾文本。字体文件(ttf)没有问题我可以将 unicode 文本写入具有不同类(PDFContentStream)的 pdf 文件。使用这个类,没有替换文本的选项,但我们可以添加新文本。

示例 Unicode 文本

咩咩

问题(地址栏)

https://drive.google.com/file/d/1DbsApTCSfTwwK3txsDGW8sXtDG_u-VJv/view?usp=sharing

我正在使用 PDFBox。请在这件事上给予我帮助.....

检查我正在使用的代码.....

    enter image description herepublic static PDDocument _ReplaceText(PDDocument document, String searchString, String replacement)
        throws IOException {
    if (StringUtils.isEmpty(searchString) || StringUtils.isEmpty(replacement)) {
        return document;
    }

    for (PDPage page : document.getPages()) {

        PDResources resources = new PDResources();
        PDFont font = PDType0Font.load(document, new File("arial-unicode-ms.ttf"));
        //PDFont font2 = PDType0Font.load(document, new File("avenir-next-regular.ttf"));
        resources.add(font);
        //resources.add(font2);
        //resources.add(PDType1Font.TIMES_ROMAN);
        page.setResources(resources);
        PDFStreamParser parser = new PDFStreamParser(page);
        parser.parse();
        List tokens = parser.getTokens();

        for (int j = 0; j < tokens.size(); j++) {
            Object next = tokens.get(j);
            if (next instanceof Operator) {
                Operator op = (Operator) next;

                String pstring = "";
                int prej = 0;

                // Tj and TJ are the two operators that display strings in a PDF
                if (op.getName().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(searchString, replacement);
                    previous.setValue(string.getBytes());
                } else if (op.getName().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();

                            if (j == prej) {
                                pstring += string;
                            } else {
                                prej = j;
                                pstring = string;
                            }
                        }
                    }

                    if (searchString.equals(pstring.trim())) {
                        COSString cosString2 = (COSString) previous.getObject(0);
                        cosString2.setValue(replacement.getBytes());

                        int total = previous.size() - 1;
                        for (int k = total; k > 0; k--) {
                            previous.remove(k);
                        }
                    }
                }
            }
        }

        // now that the tokens are updated we will replace the page content stream.
        PDStream updatedStream = new PDStream(document);
        OutputStream out = updatedStream.createOutputStream(COSName.FLATE_DECODE);
        ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
        tokenWriter.writeTokens(tokens);
        out.close();
        page.setContents(updatedStream);
    }

    return document;
}

标签: javapdfunicodepdfbox

解决方案


您的代码完全破坏了 PDF,参见。Adobe Preflight 输出:

预检输出

原因很明显,你的代码

PDResources resources = new PDResources();
PDFont font = PDType0Font.load(document, new File("arial-unicode-ms.ttf"));
resources.add(font);
page.setResources(resources);

删除预先存在的页面资源,您的替换只包含一种字体,您允许 PDFBox 任意选择其名称。

您不得删除现有资源,因为它们在您的文档中使用。


检查 PDF 页面的内容很明显,最初使用的字体T1_0T1_1的编码是单字节编码或混合的单/多字节编码;较低的单字节值似乎是类似 ASCII 的编码。

我会假设编码是WinAnsiEncoding或其子集。作为推论你的任务

从 PDF 文件中读取字符串并将其替换为 Unicode 文本

不能作为简单的替换来实现,至少不能考虑任意 Unicode 代码点。


你可以实现的是:

  • 首先通过自定义文本剥离器运行您的源 PDF,而不是提取纯文本搜索您的字符串以替换并返回它们的位置。这里有许多问题和答案,向您展示了如何确定文本剥离器子类中字符串的坐标,最近的一个是这个
  • 接下来从您的 PDF 中删除这些原始字符串。在您的情况下,类似于您上面的原始代码的方法(显然不会丢弃资源),用同样长的空格字符串替换字符串可能会起作用,即使它是一个肮脏的黑客。
  • PDFContentStream最后使用附加模式在确定的位置添加替换;为此,您的新字体添加到现有资源中。

但请注意,PDF 不是为这样使用而设计的。模板 PDF 可以用作新内容的背景,但尝试替换其中的内容通常是一种糟糕的设计,会导致麻烦。如果您需要在模板中标记位置,请使用在填写过程中可以轻松删除的注释。或者使用原生 PDF 表单技术 AcroForm 表单。


推荐阅读