我正在尝试在一个文本中替换PDF
的内容,但字母“X”未被替换。
public static void main(String[] args) {
String DEST = "/home/diego/Documentos/teste.pdf";
try {
PdfReader reader = new PdfReader("termoAdesaoCartao.pdf");
PdfDictionary dictionary = reader.getPageN(1);
PdfObject object = dictionary.getDirectObject(PdfName.CONTENTS);
if (object instanceof PRStream) {
PRStream stream = (PRStream)object;
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("Nome Completo", "A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(DEST));
stamper.close();
reader.close();
} catch (IOException | DocumentException e) {
e.printStackTrace();
}
}
答案 0 :(得分:6)
基本上OP的方法一般无法正常工作。他的代码有两个主要的误解:
他假设可以使用单个字符编码将完整的内容流从byte[]
转换为String
(所有字符串参数的文本都显示运算符清晰易读)。
这个假设是错误的:每个字体可能有自己的编码,因此如果在同一页面上使用多个字体,则显示运算符的不同文本的字符串操作数中的相同字节值可能表示完全不同的字符。实际上,字体甚至不需要包含到字符的映射,它们只需要将数值映射到字形绘制指令。
比照。第9.4.3节ISO 32000-1中的文本显示操作符:
文本显示运算符的字符串操作数应解释为标识要绘制的字形的字符代码序列。
使用简单字体时,字符串的每个字节都应被视为单独的字符代码。然后在字体的编码中查找字符代码以选择字形,如9.6.6,"字符编码"中所述。
使用复合字体(PDF 1.2),可以使用多字节代码来选择字形。在这种情况下,字符串的一个或多个连续字节应被视为单个字符代码。代码长度和从代码到字形的映射在称为CMap的数据结构中定义,
简单的PDF生成器通常只使用标准编码(这是ASCII' ish并且可能会产生像OP这样的假设)但是有越来越多的非简单PDF生成器......
他假设他可以简单地编辑文本显示操作符的字符串操作数,匹配的字形将显示在PDF查看器中。
这个假设是错误的:字体通常只支持相当有限的字符集,而显示运算符的文本只使用单个字体,即当前选择的字体。如果一个操作符的字符串参数中的代码替换了另一个没有字体中匹配字形的操作符,那么最多只能看到一个间隙!
虽然完整字体通常至少包含所有字符的字形(例如,包含其所有西欧变体的拉丁字母),但PDF允许部分嵌入字体,参见第9.6.4节字体子集在ISO 32000-1中:
PDF文档可能包含Type 1和TrueType字体的子集。
此选项同时通常仅用于嵌入现有文本中实际使用的字形的绘制指令。因此,如果嵌入字体包含一些相同类型的字符,则不能指望它们。可能有A
和C
的字形,但B
不能。
不幸的是OP没有提供他的样本PDF。但症状是:
他的电话replace("Nome Completo", "A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z")
有所作为,可以在他的截图中看到
以及他对Viacheslav Vedenin的回答
在文字
(Nome Completo)Tj
之前和(A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z)Tj
之后
但有些代码没有显示为预期的字形,也可以在上面的屏幕截图中看到
指出上述两个主要错误假设中的后一个使OP的代码失败的方向:很可能有问题的字体使用标准编码(可能 WinAnsiEncoding )但仅部分嵌入,特别是没有大写字母K
,W
,X
和Y
。
OP(已经使用iText的用户)可以使用以下iText概念,而不是盲目地编辑内容流:
PdfCleanUpProcessor
可用于删除该边界矩形中存在的所有内容; PdfStamper.getOverContent()
在这些坐标处正确添加新内容。这可能听起来很复杂,但这会解决OP方法中可见的一些额外的小误解。
答案 1 :(得分:2)
尝试使用而不是
stream.setData(new String(data).replace("Nome Completo", "A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z").getBytes());
以下代码
stream.setData(new String(data, "UTF8").replace("Nome Completo", "A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z").getBytes("UTF8"));
使用新的String(data)和getBytes()来执行jQuery.ajax()可能会导致一些错误:
字节编码和字符串
如果字节数组包含非Unicode文本,则可以将文本转换为 Unicode与其中一个String构造函数方法。相反,你 可以将String对象转换为非Unicode的字节数组 String.getBytes方法的字符。调用时的任何一个 这些方法,您指定编码标识符作为其中之一 参数。
以下示例在UTF-8和。之间转换字符 Unicode格式。 UTF-8是一种安全的Unicode传输格式 UNIX文件系统。该示例的完整源代码位于文件中 StringConverter.java。
<强>更新强>: 如果它不起作用,你可以替换代码
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("Nome Completo", "A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z").getBytes());
代码
byte[] data = PdfReader.getStreamBytes(stream);
String str = new String(data);
System.out.printLn(str);
String newStr = str.replace("Nome Completo", "A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z");
System.out.printLn(newStr);
stream.setData(newStr.getBytes());
写下你在控制台中显示的内容?
答案 2 :(得分:0)
我修改了发现的代码,它的工作原理如下
public static final String SRC = "C:/tmp/244558.pdf";
public static final String DEST = "C:/tmp/244558-2.pdf";
public static void main(String[] args) throws IOException, DocumentException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new Main().manipulatePdf(SRC, DEST);
}
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.getPageN(1);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
PdfArray refs = null;
if (dict.get(PdfName.CONTENTS).isArray()) {
refs = dict.getAsArray(PdfName.CONTENTS);
} else if (dict.get(PdfName.CONTENTS).isIndirect()) {
refs = new PdfArray(dict.get(PdfName.CONTENTS));
}
for (int i = 0; i < refs.getArrayList().size(); i++) {
PRStream stream = (PRStream) refs.getDirectObject(i);
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("Data replace", "Data").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}