花费了一周时间寻找解决方案,但仍然失败。也许知道有人:我尝试使用pdfbox将令牌替换,例如@test替换为.pdf文件中的数字123456。
它代替了它,但是在输出而不是数字中,我在正方形或彼此重叠的数字中有正方形或问号。我唯一意识到的是它取决于所选的字体。而且我不知道哪里出了错。
注意:我们假设这是一个端口问题,并在v 2.0中的Java构建上进行了测试,并且面临相同的问题。
也许有人面临类似的问题并且知道解决方法?
技术细节:
MS Word创建:
脚本:
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();
}
}
}
答案 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”条目。
在大多数情况下,通过对 TJ 运算符的字符串参数块进行连接,可以避免因字距调整而导致字距缩小的问题。剩下的问题仍然存在。
在您的示例文档中,问题在于替换为“数字显示彼此”:
原因类似于迁移指南中提到的“字体子集”问题。但是,相关的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用于页面上的第二个不同的字形,依此类推。因此,通常来说,解决方法并不那么容易。