使用pdfbox给pdf去背景图片

前面采用了Python写的pdfrw做的,发现用acrobat不能编辑。

用pdfbox工具查看发现missing xobject。

java -jar pdfbox-app-2.0.13.jar PDFDebugger out.pdf

所以改用java的pdfbox库来写

package com.c4ys;

import org.apache.pdfbox.contentstream.PDContentStream;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.pattern.PDAbstractPattern;
import org.apache.pdfbox.pdmodel.graphics.pattern.PDTilingPattern;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) throws IOException {
        if (args.length != 3) {
            usage();
        } else {
            PDDocument doc = PDDocument.load(new File(args[0]));
            if (doc.isEncrypted()) {
                System.err.println(
                        "Error: Encrypted documents are not supported .");
                System.exit(1);
            }

            for (PDPage page : doc.getPages()) {
                List<Object> newTokens = createTokensWithoutImage(page, args[2]);
                PDStream newContents = new PDStream(doc);
                writeTokensToStream(newContents, newTokens);
                page.setContents(newContents);
                processResources(page.getResources(), args[2]);
            }

            doc.save(args[1]);
            doc.close();
        }
    }

    private static List<Object> createTokensWithoutImage(PDContentStream contentStream, String im) throws IOException {
        PDFStreamParser parser = new PDFStreamParser(contentStream);
        Object token = parser.parseNextToken();
        List<Object> newTokens = new ArrayList<Object>();
        while (token != null) {
            if (token instanceof Operator) {
                Operator op = (Operator) token;
                if (op.getName().equalsIgnoreCase("do")) {
                    COSName previous = (COSName) newTokens.get(newTokens.size() - 1);
                    System.out.println(previous.getName());
                    if (previous.getName().equalsIgnoreCase(im)) {
                        // remove the argument to this operator
                        newTokens.remove(newTokens.size() - 1);
                        token = parser.parseNextToken();
                        continue;
                    }
                }
            }
            newTokens.add(token);
            token = parser.parseNextToken();
        }
        return newTokens;
    }


    private static void processResources(PDResources resources, String im) throws IOException {
        for (COSName name : resources.getXObjectNames()) {
            PDXObject xobject = resources.getXObject(name);
            if (xobject instanceof PDFormXObject) {
                PDFormXObject formXObject = (PDFormXObject) xobject;
                writeTokensToStream(formXObject.getContentStream(),
                        createTokensWithoutImage(formXObject, im));
                processResources(formXObject.getResources(), im);
            }
        }
        for (COSName name : resources.getPatternNames()) {
            PDAbstractPattern pattern = resources.getPattern(name);
            if (pattern instanceof PDTilingPattern) {
                PDTilingPattern tilingPattern = (PDTilingPattern) pattern;
                writeTokensToStream(tilingPattern.getContentStream(),
                        createTokensWithoutImage(tilingPattern, im));
                processResources(tilingPattern.getResources(), im);
            }
        }
    }

    private static void writeTokensToStream(PDStream newContents, List<Object> newTokens) throws IOException {
        OutputStream out = newContents.createOutputStream(COSName.FLATE_DECODE);
        ContentStreamWriter writer = new ContentStreamWriter(out);
        writer.writeTokens(newTokens);
        out.close();
    }


    /**
     * This will print the usage for this document.
     */
    private static void usage() {
        System.err.println("Usage: java " + Main.class.getName() + " <input-pdf> <output-pdf> <image-object-name>");
    }
}