PdfStream字节数组含括号与浮点值导致替换失效求助
解决PDF Stream文本替换失效的问题
为啥直接用String.replace没用?
PDF里的文本不是像普通文档那样存成连续字符串的,比如你要替换的DCC ID: XXX-X,在PDF的内容流里是拆成了带位置偏移的片段——就像你贴的[(TEST ID:)0.5 ( X)-15.3 (X)-15.2...]TJ这种格式:每个括号里是一小段文本,后面跟着的数字是字符间距或位置偏移。直接用String.replace找完整的目标字符串,自然匹配不到这些被拆碎的片段。
解决思路
得先把分散的文本片段拼接成完整字符串,替换完成后再重新拆回带偏移的结构,具体步骤:
- 解析PDF内容流,定位
TJ文本绘制指令,分离出其中的文本片段和偏移值 - 拼接所有文本片段得到完整字符串,执行替换操作
- 将替换后的字符串按原片段长度拆分,保留原偏移值(若替换前后长度差异大,需额外调整偏移保证排版正常)
实际代码示例(基于iText 7)
import com.itextpdf.kernel.pdf.PdfStream; import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor; import com.itextpdf.kernel.pdf.canvas.parser.listener.IRenderListener; import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo; import com.itextpdf.kernel.pdf.canvas.parser.data.ImageRenderInfo; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class PdfTextReplacer { public static byte[] replaceStreamText(PdfStream stream, String oldText, String newText) throws IOException { // 存储TJ指令的所有元素:文本片段+偏移数字 List<Object> tjElements = new ArrayList<>(); StringBuilder fullText = new StringBuilder(); // 解析内容流,提取TJ操作的内容 PdfCanvasProcessor processor = new PdfCanvasProcessor(new IRenderListener() { @Override public void beginTextBlock() {} @Override public void endTextBlock() {} @Override public void renderImage(ImageRenderInfo renderInfo) {} @Override public void renderText(TextRenderInfo renderInfo) { // 获取TJ指令对应的原始操作数 List<Object> originalTjParts = renderInfo.getTJOperands(); for (Object part : originalTjParts) { tjElements.add(part); if (part instanceof String) { fullText.append((String) part); } } } }); processor.processContent(stream.getBytes(), stream.getResources()); // 替换完整文本 String updatedFullText = fullText.toString().replace(oldText, newText); // 重构TJ指令内容,保留原偏移值 List<Object> newTjElements = new ArrayList<>(); int textCursor = 0; for (Object elem : tjElements) { if (elem instanceof String) { int partLength = ((String) elem).length(); // 按原片段长度截取新文本 String newTextPart = updatedFullText.substring(textCursor, Math.min(textCursor + partLength, updatedFullText.length())); newTjElements.add(newTextPart); textCursor += partLength; } else { // 偏移数字直接复用 newTjElements.add(elem); } } // 将重构后的TJ内容转为字节数组 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write("[".getBytes()); for (int i = 0; i < newTjElements.size(); i++) { Object currentElem = newTjElements.get(i); if (currentElem instanceof String) { // 转义PDF字符串中的特殊字符 String escapedStr = ((String) currentElem) .replace("\\", "\\\\") .replace("(", "\\(") .replace(")", "\\)"); outputStream.write(("(" + escapedStr + ")").getBytes()); } else { outputStream.write(currentElem.toString().getBytes()); } if (i != newTjElements.size() - 1) { outputStream.write(" ".getBytes()); } } outputStream.write("] TJ".getBytes()); return outputStream.toByteArray(); } }
注意事项
- 示例基于iText 7,若使用旧版本iText,API需对应调整
- 如果替换前后文本长度差异较大,直接保留原偏移会导致排版错乱,此时需要结合PDF字体信息重新计算偏移值
- 实际场景中可能存在多个
TJ指令,需遍历整个文本块的所有指令进行处理
内容的提问来源于stack exchange,提问作者sandeep sangole
相关产品推荐
相关产品推荐

