You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用iText7彻底移除PDF中的纯文本对象?

使用iText7彻底移除PDF中的所有文本(替代PDFCleanUp方案)

你可以通过直接解析并重写PDF的内容流,跳过所有文本相关的绘制操作,来彻底移除PDF中的文本对象,避免PDFCleanUp添加背景框的问题。以下是具体实现方案:

核心思路

遍历PDF的每一页及嵌套的Form XObject,监听内容流中的事件:

  • 识别文本块的开始(BEGIN_TEXT)和结束(END_TEXT)标记,跳过该区间内的所有文本渲染操作
  • 保留并重新绘制非文本内容(如路径、图像、阴影等)

Java代码实现

主处理逻辑

import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor;
import com.itextpdf.kernel.pdf.canvas.parser.listener.IPdfCanvasListener;
import com.itextpdf.kernel.pdf.canvas.parser.data.IEventData;
import com.itextpdf.kernel.pdf.canvas.parser.EventType;
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.PathRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.ImageRenderInfo;

import java.io.IOException;

public class RemovePdfText {
    public static void main(String[] args) throws IOException {
        try (PdfReader reader = new PdfReader("input.pdf");
             PdfWriter writer = new PdfWriter("output.pdf");
             PdfDocument pdfDoc = new PdfDocument(reader, writer)) {

            // 遍历所有页面
            for (int pageNum = 1; pageNum <= pdfDoc.getNumberOfPages(); pageNum++) {
                PdfPage page = pdfDoc.getPage(pageNum);
                processPageContent(page, pdfDoc);
                processPageFormXObjects(page, pdfDoc);
            }
        }
    }

    // 处理页面直接内容流
    private static void processPageContent(PdfPage page, PdfDocument pdfDoc) {
        PdfCanvasProcessor processor = new PdfCanvasProcessor(new TextRemovalListener(page.newContentStreamBefore(), page.getResources(), pdfDoc));
        processor.processPageContent(page);
        page.removeContentStream();
    }

    // 处理页面资源中的Form XObject
    private static void processPageFormXObjects(PdfPage page, PdfDocument pdfDoc) {
        PdfResources resources = page.getResources();
        if (resources == null) return;

        PdfDictionary xObjects = resources.getPdfObject().getAsDictionary(PdfName.XObject);
        if (xObjects == null) return;

        for (PdfName xObjName : xObjects.keySet()) {
            PdfStream xObjStream = xObjects.getAsStream(xObjName);
            if (PdfName.Form.equals(xObjStream.getAsName(PdfName.Subtype))) {
                PdfFormXObject formXObject = new PdfFormXObject(xObjStream);
                processFormXObjectContent(formXObject, pdfDoc);
            }
        }
    }

    // 处理Form XObject内容流
    private static void processFormXObjectContent(PdfFormXObject formXObj, PdfDocument pdfDoc) {
        PdfCanvasProcessor processor = new PdfCanvasProcessor(new TextRemovalListener(formXObj.getPdfObject(), formXObj.getResources(), pdfDoc));
        processor.processContent(formXObj.getPdfObject().getBytes(), formXObj.getResources());
        formXObj.getPdfObject().setData(processor.getCanvas().getContentStreamBytes());
    }

    // 自定义监听器:移除文本,保留其他内容
    private static class TextRemovalListener implements IPdfCanvasListener {
        private final PdfCanvas canvas;
        private boolean inTextBlock = false;

        public TextRemovalListener(PdfStream contentStream, PdfResources resources, PdfDocument pdfDoc) {
            this.canvas = new PdfCanvas(contentStream, resources, pdfDoc);
        }

        @Override
        public void eventOccurred(IEventData data, EventType type) {
            switch (type) {
                case BEGIN_TEXT:
                    inTextBlock = true;
                    break;
                case END_TEXT:
                    inTextBlock = false;
                    break;
                case TEXT_RENDER_INFO:
                    // 跳过所有文本渲染操作
                    break;
                case PATH_RENDER_INFO:
                    if (!inTextBlock) {
                        PathRenderInfo pathInfo = (PathRenderInfo) data;
                        canvas.addPath(pathInfo.getPath());
                        switch (pathInfo.getOperation()) {
                            case STROKE:
                                canvas.stroke();
                                break;
                            case FILL:
                                canvas.fill();
                                break;
                            case FILL_STROKE:
                                canvas.fillStroke();
                                break;
                        }
                    }
                    break;
                case IMAGE_RENDER_INFO:
                    if (!inTextBlock) {
                        ImageRenderInfo imageInfo = (ImageRenderInfo) data;
                        PdfImageXObject image = imageInfo.getImage();
                        canvas.addImage(image, imageInfo.getImageMatrix(), false);
                    }
                    break;
                // 可根据需求添加其他非文本类型的处理(如Shading等)
                default:
                    if (!inTextBlock) {
                        // 其他非文本操作可按需处理
                    }
                    break;
            }
        }

        @Override
        public EventType[] getSupportedEvents() {
            return new EventType[]{
                    EventType.BEGIN_TEXT, EventType.END_TEXT,
                    EventType.TEXT_RENDER_INFO, EventType.PATH_RENDER_INFO,
                    EventType.IMAGE_RENDER_INFO
            };
        }
    }
}

关键说明

  1. 依赖引入:确保项目中包含iText7核心库,Maven依赖示例:
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>kernel</artifactId>
    <version>7.2.5</version>
</dependency>
  1. 嵌套内容处理:代码同时处理了页面直接内容和Form XObject中的文本,覆盖大多数PDF的场景
  2. 内容保留:仅移除PdfName.Text类型的文本绘制操作,保留所有路径、图像等非文本元素,不会修改原始背景

C#版本参考

逻辑与Java一致,语法调整为C#风格,例如使用PdfCanvasProcessor和IPdfCanvasListener接口,处理页面和Form XObject的方式类似。

内容的提问来源于stack exchange,提问作者argledhel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 18:45:31