为什么使用pdfbox在输出文件中显示正方形而不是符号

时间:2018-11-02 07:48:05

标签: c# pdf pdfbox pdf-conversion

花费了一周时间寻找解决方案,但仍然失败。也许知道有人:我尝试使用pdfbox将令牌替换,例如@test替换为.pdf文件中的数字123456。

它代替了它,但是在输出而不是数字中,我在正方形或彼此重叠的数字中有正方形或问号。我唯一意识到的是它取决于所选的字体。而且我不知道哪里出了错。

注意:我们假设这是一个端口问题,并在v 2.0中的Java构建上进行了测试,并且面临相同的问题。

也许有人面临类似的问题并且知道解决方法?

技术细节:

MS Word创建:

  • 只需右键单击桌面
  • 从创建新点中选择Microsoft Word文档
  • 打印内部文本:@test

脚本:

private void ReplaceTextInPdf(string inputPath, string outputPath) {
            PDDocument doc = null;
            try {
                File input = new File(inputPath);
                doc = PDDocument.loadNonSeq(input, null);
                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 is PDFOperator) {
                            PDFOperator op = (PDFOperator)next;
                            //Tj and TJ are the two operators that display
                            //strings in a PDF
                            if (op.getOperation() == "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 tempString = previous.getString();

                                tempString = tempString.replace("@test", "123456");

                                previous.reset();
                                previous.append(tempString.getBytes());
                            } else if (op.getOperation() == "TJ") {
                                String tempString = "";
                                COSString cosString = null;
                                COSArray previous = (COSArray)tokens.get(j - 1);
                                for (int k = 0; k < previous.size(); k++) {
                                    Object arrElement = previous.getObject(k);
                                    if (arrElement is COSString) {
                                        cosString = (COSString)arrElement;
                                        tempString += cosString.getString();
                                        cosString.reset();
                                    }
                                }

                                if (tempString != null && tempString.trim().length() > 0) {

                                    tempString = tempString.replace("@test", "123456");

                                    for (int k = 0; k < previous.size(); k++) {
                                        Object arrElement = previous.getObject(k);
                                        if (arrElement is COSString) {
                                            cosString.reset();
                                            cosString.append(tempString.getBytes("ISO-8859-1"));
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }

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

                doc.save(outputPath);
            } finally {
                if (doc != null) {
                    doc.close();
                }
            }
        }

1 个答案:

答案 0 :(得分:1)

一般

首先,您使用的代码仅在合适的情况下有效,即仅适用于以特殊方式生成的PDF。尽管早些时候的PDF通常是用这种方式创建的,但如今它们已经不再使用了。这导致删除了PDFBox示例,该示例从PDFBox 2.0的源代码库中获取。

迁移指南中的匹配条目说明:

  

为什么将ReplaceText示例删除?

     

ReplaceText示例已删除,因为它给人一种错误的幻想,即可以轻松替换文本。如内容流的摘录所示,单词经常被拆分:

[ (Do) -29 (c) -1 (umen) 30 (tation) ] TJ
     

字体子集还会出现其他问题:例如,如果仅使用a,b和c的字形,则它们将被编码为十六进制0、1和2,因此您将找不到“ abc”。此外,您不能将“ c”替换为“ d”,因为它不属于子集。

     

您可能还会遇到连字问题,例如“ ff”,“ fl”,“ fi”,“ ffi”,“ ffl”,可以用多种字体的单个代码表示。要自己理解这一点,请使用PDFDebugger查看任何文件,并查看页面的“ Contents”条目。

     

另请参阅PDFBox 2.0 RC3 -- Find and replace text

Migration to PDFBox 2.0.0

在大多数情况下,通过对 TJ 运算符的字符串参数块进行连接,可以避免因字距调整而导致字距缩小的问题。剩下的问题仍然存在。

以您的示例文档为例

在您的示例文档中,问题在于替换为“数字显示彼此”:

screen shot original ==> screen shot with replacement

原因类似于迁移指南中提到的“字体子集”问题。但是,相关的TTF字体程序未嵌入,因此不是真正的“字体子集”问题。但是,PDF中存储的与字体相关的信息仅适用于原始PDF中实际使用的字形,即“ @”,“ e”,“ s”和“ t”,而不适用于替换字形(即数字)从1到6。

与当前情况相关的特定于字形的信息是字形宽度:仅对于最初使用的字形,它是正确给出的,对于所有其他字形,给定宽度为0!结果:绘制了一个替换字形后,绘制下一个字形的位置没有适当移动,而是保持不变(适用于宽度为0的字形),因此绘制的下一个字形从相同位置开始,有效地绘制了所有替换字形都相互重叠。

(更具体地说,该字体的widths数组如下所示:

[ 250 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 921 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 0 0 0 0 0 0 0 0 0 0 0 0 0 389 278] 

使用 WinAnsiEncoding 对'@','e','s'和't'进行编码,并使用从'@'到't'范围内的字体。)< / p>

在这种特殊情况下,您可以通过在Word模板中不可见的某个地方(例如白色)在字符串上打印一个字符串,其中包含字体中的所有字符,您可能希望将其替换为占位符。

不过,一般而言,编码不需要像 WinAnsiEncoding 这样的ASCII码,而是可以完全不同,甚至可以弥补这种情况,例如#1用于页面上的第一个字形,#2用于页面上的第二个不同的字形,依此类推。因此,通常来说,解决方法并不那么容易。