如何区分PDF背景色与文本色并通过PDFBox修复不可见文本
使用PDFBox修复PDF文本与背景同色不可见问题
核心思路
PDF中出现文本与背景同色不可见的情况,本质是文本的非描边/描边颜色和背景填充颜色一致。我们可以通过解析PDF流提取文本对象,重建PDF时强制重置文本颜色的方式,让文本可见。
具体实现步骤
1. 提取文本对象信息(可选,用于确认不可见文本)
通过继承PDFStreamEngine,拦截文本绘制操作符,记录文本的内容、位置、当前颜色等信息到自定义的TextObjectInfo类中:
public class TextObjectExtractor extends PDFStreamEngine { private final List<TextObjectInfo> textInfos = new ArrayList<>(); public TextObjectExtractor() throws IOException { super(); // 注册文本相关操作符处理器 addOperator(new ShowText()); addOperator(new ShowTextAdjusted()); addOperator(new SetTextFont()); addOperator(new SetTextColor()); } @Override protected void processOperator(Operator operator, List<COSBase> operands) throws IOException { String opName = operator.getName(); // 捕获所有文本绘制操作 if ("Tj".equals(opName) || "'".equals(opName) || "\"".equals(opName) || "TJ".equals(opName)) { PDGraphicsState gs = getGraphicsState(); PDColor textColor = gs.getNonStrokingColor(); // 填充TextObjectInfo的属性(文本内容、位置矩阵、颜色等) textInfos.add(new TextObjectInfo(/* 传入对应参数 */)); } super.processOperator(operator, operands); } public List<TextObjectInfo> getTextInfos() { return textInfos; } }
2. 自定义PDF重建器,强制设置文本颜色
编写SimplePdfRegenerator类,在重建PDF流时,拦截文本绘制操作,强制将文本颜色设置为固定值(比如黑色),同时保留原有背景绘制逻辑:
public class SimplePdfRegenerator extends PDFStreamEngine { private final PDPageContentStream contentStream; // 固定文本颜色:黑色(RGB值[0,0,0]) private final PDColor FIXED_TEXT_COLOR = new PDColor(new float[]{0, 0, 0}, PDDeviceRGB.INSTANCE); public SimplePdfRegenerator(PDPage page, PDPageContentStream contentStream) throws IOException { super(); this.contentStream = contentStream; // 拦截文本颜色设置与绘制操作 addOperator(new SetTextColor()); // 处理单个文本绘制操作Tj addOperator(new ShowText() { @Override public void process(Operator operator, List<COSBase> operands) throws IOException { // 绘制文本前强制设置固定颜色 contentStream.setNonStrokingColor(FIXED_TEXT_COLOR); // 执行原文本绘制逻辑 super.process(operator, operands); } }); // 处理多行文本绘制操作TJ addOperator(new ShowTextAdjusted() { @Override public void process(Operator operator, List<COSBase> operands) throws IOException { contentStream.setNonStrokingColor(FIXED_TEXT_COLOR); super.process(operator, operands); } }); // 处理其他文本快捷操作符'和" addOperator(new OperatorProcessor() { @Override public void process(Operator operator, List<COSBase> operands) throws IOException { contentStream.setNonStrokingColor(FIXED_TEXT_COLOR); // 调用默认处理器执行原操作 OperatorProcessor defaultProcessor = new OperatorProcessor(); defaultProcessor.process(operator, operands); } }); } @Override protected void processOperator(Operator operator, List<COSBase> operands) throws IOException { // 保留背景、路径等非文本操作的原有逻辑 super.process(operator, operands); } }
3. 执行PDF修复流程
加载原始PDF,遍历页面提取文本信息(可选),然后用重建器生成新的PDF:
public class PdfFixer { public static void main(String[] args) throws IOException { File inputFile = new File("input.pdf"); File outputFile = new File("fixed_output.pdf"); try (PDDocument document = PDDocument.load(inputFile)) { for (PDPage page : document.getPages()) { // 可选:提取文本信息,排查不可见文本 TextObjectExtractor extractor = new TextObjectExtractor(); extractor.processPage(page); List<TextObjectInfo> textInfos = extractor.getTextInfos(); // 重建页面内容,强制设置文本颜色 try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true, true)) { SimplePdfRegenerator regenerator = new SimplePdfRegenerator(page, contentStream); regenerator.processPage(page); } } document.save(outputFile); } } }
效果验证
对比修复前后的文档状态:
- 原始背景:

- 无背景文本(黑色):

- 输入文档(文本可见状态):

- 修复前输出(文本银色不可见):

- 修复后输出(文本为固定黑色,可见):

内容的提问来源于stack exchange,提问作者Nitish Kumar
相关产品推荐
相关产品推荐

