连接 pdf 而不展平但保留字段

Concatenate pdf without flattening but preserve fields

我正在尝试填写模板 pdf 并在末尾添加另一个 pdf。 将页面添加到另一个 pdf 没有问题,但问题是,当我这样做时,即使我不使用 stamper.setFormFlattening(true).

,我的字段也会丢失

这是我的代码:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSmartCopy;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;

public class ForStack {

    public static void main(String[] args) throws IOException, DocumentException, ParseException {
        createContractWithMoreFile();
    }

    public static void createContractWithMoreFile()
            throws IOException, DocumentException, ParseException {

        String linkPDF = "resources/pdfs/User.pdf";

        PdfReader reader = new PdfReader(linkPDF);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfStamper stamper = new PdfStamper(reader, baos);

        PdfWriter writer = stamper.getWriter();
        writer.setPdfVersion(PdfWriter.VERSION_1_7);
        AcroFields form = stamper.getAcroFields();

        form.setField("Name", "Jhon");
        stamper.close();
        String out = "results/merged.pdf";

        List<byte[]> listOfPdfFiles = new ArrayList<>();
        listOfPdfFiles.add(baos.toByteArray());

        byte[] informativaPrivacy = getPdfByteArray("resources/pdfs/second.pdf");
        listOfPdfFiles.add(informativaPrivacy);

        concatenatePdfs(listOfPdfFiles, new File(out));

        baos.close();
        reader.close();

    }

    public static byte[] getPdfByteArray(String filePath) {
        File fileP = new File(filePath);
        byte[] result;
        try {
            result = FileUtils.readFileToByteArray(fileP);
            return result;
        } catch (IOException e) {
            return null;
        }
    }

    public static void concatenatePdfs(List<byte[]> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
        Document document = new Document();
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        PdfCopy copy = new PdfSmartCopy(document, outputStream);
        document.open();
        for (byte[] inFile : listOfPdfFiles) {
            PdfReader reader = new PdfReader(inFile);
            copy.addDocument(reader);
            reader.close();
        }
        document.close();
    }
}

这是我正在使用的文件

  1. User

  2. second

输出文件不是我想要的: result file

那么为什么输出的 pdf 丢失了我的字段?没有串联就没有扁平化.....

正如你在我的结果文件中看到的那样,没有该字段,所以如果你想再次看到它,我必须使用 adobe Acrobat ,使用 -> View(Vista)-> Tools(Impostazioni)- > 创建表格 (Prepara Modulo)。 但是如果我这样做并尝试退出 pdf,Adobe 会要求我保存它更改的 pdf,这不是我想要的。

我想要的输出pdf在这里: Output File That I want 结果文件中丢失的字段

您的代码中有很多错误。比如:你不需要org.w3c.dom.Document,你需要com.itextpdf.text.Document;此错误导致您的代码甚至无法编译。

我修正了错误,最后得到了这个 SSCCE:

package sandbox.merge;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSmartCopy;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;

public class ForStack {

    public static void main(String[] args) throws IOException, DocumentException, ParseException {
        createContractWithMoreFile();
    }

    public static void createContractWithMoreFile()
            throws IOException, DocumentException, ParseException {

        String linkPDF = "resources/pdfs/User.pdf";

        PdfReader reader = new PdfReader(linkPDF);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfStamper stamper = new PdfStamper(reader, baos);

        PdfWriter writer = stamper.getWriter();
        writer.setPdfVersion(PdfWriter.VERSION_1_7);
        AcroFields form = stamper.getAcroFields();

        form.setField("Name", "Jhon");
        stamper.setFormFlattening(true);
        stamper.close();
        String out = "results/merged.pdf";

        List<byte[]> listOfPdfFiles = new ArrayList<>();
        listOfPdfFiles.add(baos.toByteArray());

        byte[] informativaPrivacy = getPdfByteArray("resources/pdfs/second.pdf");
        listOfPdfFiles.add(informativaPrivacy);

        concatenatePdfs(listOfPdfFiles, new File(out));

        baos.close();
        reader.close();

    }

    public static byte[] getPdfByteArray(String filePath) {
        File fileP = new File(filePath);
        byte[] result;
        try {
            result = FileUtils.readFileToByteArray(fileP);
            return result;
        } catch (IOException e) {
            return null;
        }
    }

    public static void concatenatePdfs(List<byte[]> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
        Document document = new Document();
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        PdfCopy copy = new PdfSmartCopy(document, outputStream);
        document.open();
        for (byte[] inFile : listOfPdfFiles) {
            PdfReader reader = new PdfReader(inFile);
            copy.addDocument(reader);
            reader.close();
        }
        document.close();
    }
}

我只能通过删除以下行来重现您提到的问题:

stamper.setFormFlattening(true);

您的代码中缺少该行,并解释了表单未展平的原因。

总结:

当您展开表单时,您会得到:

曾经是字段 "Name" 的地方,我们看到了值 "Jhon",但是字段本身已经消失了:这就是扁平化的意义所在:您删除了所有交互性。

当你不展平表格时,你有这个:

交互场还在,没变平。它填写了值 "Jhon".

OP 似乎想像第一个屏幕截图那样扁平化表单,同时像第二个屏幕截图那样保留字段。这是一个矛盾。如果需要答案,OP 应该阐明预期的内容。

iText 版本

顺便说一下:我用 iText 5.5.13 来测试这个。请注意,除非您是付费客户,否则不再支持 iText 5。当前版本是 iText 7.1.2,但在 7.1.2 中,PdfStamper class 不再存在。在 iText 7 中填写表单和合并文档的方式有所不同。

Bruno 的回答最初假设来自 OP 原始代码的 stamper.setFormFlattening(true) 调用表明表单应该被展平。事实证明情况并非如此,这些字段应该保留。

因此,Bruno 删除了表单扁平化线并指出结果现在是可编辑的,即存在表单字段。但是OP仍然坚持说他们走了。

事实证明,他们都是对的,各有千秋。区别:表单字段在输出中作为页面上的 小部件注释 AcroForm 表单定义 不见了。

要制作 iText 5.5.x PdfCopy 实例,请在目标文档中创建一个 AcroForm 表单定义,其中包含所有复制源文档,必须 激活其 mergeFields 模式 !

如果您想知道为什么默认情况下此模式未激活:它有一个缺点,所有源 PdfReader 对象必须保持打开状态,直到目标 PdfCopy 实例关闭,这可能会导致代码的内存占用量大大增加。

要在 mergeFields 模式下工作,OP 的 concatenatePdfs 方法

void concatenatePdfs(List<byte[]> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
    Document document = new Document();
    FileOutputStream outputStream = new FileOutputStream(outputFile);
    PdfCopy copy = new PdfSmartCopy(document, outputStream);
    document.open();
    for (byte[] inFile : listOfPdfFiles) {
        PdfReader reader = new PdfReader(inFile);
        copy.addDocument(reader);
        reader.close();
    }
    document.close();
}

必须这样重写:

void concatenatePdfs(List<byte[]> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
    Document document = new Document();
    FileOutputStream outputStream = new FileOutputStream(outputFile);
    PdfCopy copy = new PdfSmartCopy(document, outputStream);
    copy.setMergeFields();
    document.open();
    List<PdfReader> pdfReaders = new ArrayList<>();
    for (byte[] inFile : listOfPdfFiles) {
        PdfReader reader = new PdfReader(inFile);
        copy.addDocument(reader);
        pdfReaders.add(reader);
    }
    document.close();
    pdfReaders.forEach(r -> r.close());
}

(CopyWithField方法concatenatePdfs)

如您所见,mergeFields模式由copy.setMergeFields()激活,源PdfReader实例添加到[=20=后不会立即关闭],而是收集在 pdfReaders 中,并且仅在 copy 关闭后关闭(在 document.close() 期间隐式关闭)。