【问题标题】:iText How to create multi-page document from a fillable templateiText 如何从可填充模板创建多页文档
【发布时间】:2017-03-15 15:51:26
【问题描述】:

我正在尝试在 iText 中创建一个带有填写表格的多页 PDF 文档,每个人一份。我在互联网上查找了如何执行此操作的示例,并在我的解决方案中使用了这些示例。

PDF 模板是使用 Adob​​e Acrobat Pro 创建的。

我已经能够使用 iText 从我的模板中成功填写并返回单页 PDF 文档,但多文档流程似乎无法正常工作。

这是我的程序,展示了我正在尝试做的事情:

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

import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.NumberFormat;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;

public class ITextTest
{
    public static final String TEMPLATE =
    "C:\\RAD7_5\\iTextTest\\iTextTest\\input\\LS213_1.pdf";

    public static void main(String[] args)
    {
        ITextTest iTextTest = new ITextTest();
        iTextTest.doItextTest();
    }

    public void doItextTest()
    {
        try
        {
            PdfReader pdfReader;
            PdfStamper pdfStamper;
            ByteArrayOutputStream baos;

            Document document = new Document();
            PdfSmartCopy pdfSmartCopy = new PdfSmartCopy(document,
                    new FileOutputStream("C:\\RAD7_5\\iTextTest\\iTextTest\\output\\LS213_1MultiTest.pdf"));

            DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
            Date currDate = new Date();
            NumberFormat numberFormat = NumberFormat.getCurrencyInstance();
            double amount = 4127.29d;

            document.open();

            for(int i = 1; i <= 5; i++)
            {
                pdfReader = new PdfReader(TEMPLATE);
                baos = new ByteArrayOutputStream();
                pdfStamper = new PdfStamper(pdfReader, baos);

                AcroFields acroFields = pdfStamper.getAcroFields();

                //key statement 1
                acroFields.setGenerateAppearances(true);

                //acroFields.setExtraMargin(5, 5);
                acroFields.setField("Name and Address", "John Doe\n123 Anywhere St.\nAnytown, USA 12345");
                acroFields.setField("Case Number", "123456789");
                acroFields.setField("Employer", "Employer Co., Inc.\n456 Anyhow ln.\nAnyville, USA 67890");
                acroFields.setField("Date", dateFormat.format(currDate));
                acroFields.setField("Name", "John Doe");
                acroFields.setField("restitution check No", "65432" + i);
                acroFields.setField("in the sum of", numberFormat.format(amount));

                //key statement 2
                pdfStamper.setFormFlattening(false);

                pdfStamper.close();
                pdfReader.close();

                pdfReader = new PdfReader(baos.toByteArray());
                pdfSmartCopy.addPage(pdfSmartCopy.getImportedPage(pdfReader, 1));
                pdfSmartCopy.freeReader(pdfReader);
                pdfReader.close();
            }

            document.close();
        }
        catch(DocumentException dex)
        {
            dex.printStackTrace();
            System.exit(1);
        }
        catch(IOException ex)
        {
            ex.printStackTrace();
            System.exit(1);
        }
    }
}

在上面的代码中,可以看到影响填充模板结果的两个关键语句:

acroFields.setGenerateAppearances(true);
pdfStamper.setFormFlattening(false);

使用上述两个语句,如果我将第一个设置为 true,第二个设置为 false,它会填充字段,但它们与标签未对齐。此外,在第一个模板副本之后,之后的每个副本由于某种原因都有一些未填写的字段。

如果我将它们都设置为 true:

acroFields.setGenerateAppearances(true);
pdfStamper.setFormFlattening(true);

它设置所有模板副本中的所有字段。这对我来说是迄今为止最成功的结果,但是填充的字段仍然与标签不对齐,并且如果应用程序中的数据错误,将表单展平设置为 true 不再允许用户在之后手动更正字段。

如果我将第一个设置为 false,第二个设置为 true:

acroFields.setGenerateAppearances(false);
pdfStamper.setFormFlattening(true);

所有字段都是空白的(最坏的结果)。

如果我将它们都设置为 false:

acroFields.setGenerateAppearances(false);
pdfStamper.setFormFlattening(false);

然后字段被填充并与标签右对齐。但是由于某种原因,这些字段显示为空白,直到您单击它们。并且在后续页面中删除某些字段的问题就像在真假场景中一样(提到的第一个场景)。

我想知道是否可以在不对齐字段值、不展平字段以及在后续页面上不丢失字段的情况下使其工作。

我知道您可以使用之后调整边距

acroFields.setExtraMargin(extraMarginLeft, extraMarginTop)

但使用

acroFields.setGenerateAppearances(false)

无需调整页边距即可完美处理单个表单,我希望它也适用于多页文档。

另外,使用

acroFields.setGenerateAppearances(true)

当您单击它时,会使文本在文本框中移动并稍微移位。单页文档和多页文档都会发生这种情况。使用 setGenerateAppearances(true) 设置字段时,使用 Adob​​e Pro 创建的 iText 或 PDF 模板中似乎存在错误。

我目前使用的是 iText 5.5.8。

对于这个问题的任何帮助将不胜感激。感谢您抽出宝贵时间阅读本文。

【问题讨论】:

    标签: java templates pdf itext


    【解决方案1】:

    这是一个很长的问题,我想这让人们很难回答。我也没有确定的答案,因为我无法重现该问题。不过,我可以澄清几件事。

    1.在 PDF 中,一个字段可以对应多个小部件注释。一个字段只能有一个值。

    假设您有一个 PDF 表单,其中包含一个名为“名称”的字段。该字段可能出现在文档的不同位置。例如:如果一个表单有多个页面,“名称”字段可以对应每个页面上的小部件注释(例如,在标题中)。

    “姓名”字段只能有一个值,例如:“Charles Carrington”。如果该字段对应于不同的小部件注释,则这些可视化中的每一个都应显示相同的名称。

    字段“名称”不可能在一个页面上具有名称“Charles Carrington”,而在另一页面上具有“Bruno Lowagie”。

    为什么这对您很重要?

    您尝试了setFormFlattening()

    如果您将此方法与值 false 一起使用,那么您做错了几件事:

    1. 你没有告诉PdfSmartCopy你正在合并表单:How to merge forms from different files into one PDF?
    2. 您违反了“1 个字段 = 1 个值”的规则,因为您首先使用 不同 值填写了 不同 表单中的字段(例如,表单 1:name = Charles Carrington ; 表格 2:姓名 = Bruno Lowagie),然后您将这些表格合并到一个表格中,您突然希望一个字段具有不同的值(例如,合并表格:姓名 = 第 1 页的 Charles Carrington;姓名 = 第 2 页的 Bruno Lowagie)。这违反了 ISO-32000-1。

    您可以通过以下方式避免此问题:

    • 如果保留交互性对您很重要,请重命名字段。
    • 展平字段:在这种情况下,所有字段都将被删除。相反,您添加值的外观。失去所有交互性。

    2。在 PDF 中,一个字段有一个值,但它也可以有一个或多个外观。

    假设您有一个 PDF 表单,其中包含一个名为“birthdate”的字段,该字段的值为“1970-06-10”(这也是它在数据库中的存储方式)。但是,当您填写 PDF 文档中的字段时,您希望它显示为“1970 年 6 月 10 日”。

    这是可能的。字段字典的/V 键的值将是PDF 字符串1970-06-10。但是,/DA 键将定义一个显示“1970 年 6 月 10 日”的外观

    甚至可以有一个具有单个值 (1970-06-10) 的字段与具有不同外观的不同小部件注释相对应:“June 10, 1970”、“10 juin 1970”、“10 juni 1970”、等等。

    为什么这对您很重要?

    你一直在尝试setGenerateAppearances()

    当您将此方法与值false 一起使用时,您指示iText 省略/DA:不创建外观。展平表单时,字段为空。当您不展平表单时,PDF 查看器将创建外观。由于 Adob​​e 和其他供应商在呈现 PDF 的方式上并不总是一致,因此很难预测这种外观会是什么样子。一个查看器将在为小部件注释定义的矩形内的一个位置显示值;另一个查看器将以不同的偏移量显示它。

    当您将此方法与值 true 一起使用时,您指示 iText 以一致的方式创建外观。

    但是:如果不展平表格,可以达到我对问题Why does iText enter a cross symbol when CheckType style is check mark?的回答中描述的效果 在此示例中,您会看到复选框的外观取决于该字段是否突出显示。文本字段也是如此:无论您是否选择它,外观都可能不同。另外:当您更改值时,您将获得查看器创建的外观。例如。当您因为要更改它而单击“1970 年 6 月 10 日”时,您会突然看到“1970-06-10”,因为这是为字段存储的值,并且该值是由查看器生成的。

    如果您将表单展平,则 iText 会创建外观并删除所有交互性。在这种情况下,查看器不会创建任何外观:表单中没有更多字段。

    3. iText 始终以相同的方式创建扁平外观。

    这是阅读您的问题后仍然存在的谜团。您声称填充和展平单个表格时的外观与填充和展平多个表格然后连接它们时的外观不同。我无法重现该问题。对于这个问题,我能给你的唯一答案是:它对我有用。(如果你不相信我,请watch this tutorial。)

    请根据 1.2. 中提供的信息调整您的示例,如果问题仍然存在,请发布一个新的、简短的问题。

    【讨论】:

    • 您提供的第一个链接(“如何将不同文件中的表单合并到一个 PDF 中?”)解决了连续页面中的字段被清除的问题。重命名字段有助于保留用户交互性。您对 setGenerateAppearances() 的解释解决了未对齐字段的问题。如果我们最终使用多个 PDF 查看器(Adobe Reader 以外的查看器),我会将其设置为 true 并使用 setExtraMargin() 语句来调整对齐方式。您的回复非常有帮助,现在我们的文件正在按预期工作。感谢您的帮助。
    猜你喜欢
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    相关资源
    最近更新 更多