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

时间:2018-06-13 08:45:17

标签: pdf itext flatten

我正在尝试填写模板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

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

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

    正如你在我的结果文件中看到的那样,没有字段,所以如果你想再次看到它,我必须使用adobe Acrobat,使用 - &gt;查看(Vista系统) - &GT;工具(Impostazioni) - &GT;创建表单(Prepara Modulo)。 但如果我这样做并尝试退出pdf,Adobe会要求我保存它改变的pdf,而不是我想要的。

    The result

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

2 个答案:

答案 0 :(得分:1)

布鲁诺的回答最初假设来自OP原始代码的stamper.setFormFlattening(true)来电表示该表格应该被展平。事实证明情况并非如此,这些领域仍然存在。

因此,布鲁诺删除了表格展平线并指出现在的结果是可编辑的,即表格字段存在。但是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实例在添加到copy之后不会立即关闭,而是收集在pdfReaders中,仅在copy关闭后关闭(在document.close()期间隐式关闭)。

答案 1 :(得分:0)

您的代码中存在大量错误。例如:您不需要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);

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

<强>总结

当你压扁表格时,你有这个:

enter image description here

曾经是字段"Name",我们看到了值"Jhon",但字段本身已经消失了:这就是展平:你删除了所有的交互性。

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

enter image description here

互动领域仍然存在,没有发现。它填充了值"Jhon"

OP似乎想像第一次屏幕截图那样压平表格,同时保持第二次屏幕截图中的字段。这是一个矛盾。如果需要回答,OP应该澄清预期的结果。

iText版本

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