简体   繁体   English

iText如何从可填充模板创建多页文档

[英]iText How to create multi-page document from a fillable template

I am trying to create a multi-page PDF document in iText with filled forms, one for each person. 我正在尝试在iText中创建一个多页PDF文档,其中包含填充表格,每个人一个。 I have looked up examples of how to do this on the internet and used those examples in my solution. 我查找了有关如何在Internet上执行此操作的示例,并在我的解决方案中使用了这些示例。

The PDF template is one created with Adobe Acrobat Pro. PDF模板是使用Adobe Acrobat Pro创建的。

I have been able to successfully fill in and return a single-page PDF document from my template using iText, but the multi-document process doesn't seem to work right. 我已经能够使用iText从我的模板成功填写并返回单页PDF文档,但是多文档处理似乎无法正常工作。

This my program that demonstrates what I am trying to do: 这个程序演示了我正在尝试做的事情:

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);
        }
    }
}

In the code above, you can see two key statements that affect the result of the filled template: 在上面的代码中,您可以看到两个影响填充模板结果的关键语句:

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

With the above two statements, if I set the first one to true and the second one to false, it fills the fields, but they are misaligned with the labels. 使用以上两个语句,如果我将第一个设置为true,将第二个设置为false,它将填充字段,但是它们与标签未对齐。 Also, after the first template copy, each copy after that has some unfilled fields for some reason. 同样,在第一个模板副本之后,由于某种原因,此后的每个副本都有一些未填写的字段。

If I set them both to true: 如果我将它们都设置为true:

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

it sets all of the fields in all of the template copies. 它设置所有模板副本中的所有字段。 This is the most successful result for me so far, but the filled fields are still misaligned with the labels and setting form flattening to true no longer allows a user to correct a field manually afterwards if the data in the application is wrong. 到目前为止,这对我来说是最成功的结果,但是,填充的字段仍与标签未对齐,并且如果应用程序中的数据有误,则展平为true的设置表单将不再允许用户随后手动更正字段。

If I set the first one to false and the second one to true: 如果我将第一个设置为false,第二个设置为true:

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

all of the fields are completely blank (worst result). 所有字段均完全空白(最差结果)。

If I set them both to false: 如果我将它们都设置为false:

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

then the fields are filled and appear in the right alignment with the labels. 然后字段将被填充并与标签正确对齐。 But the fields appear blank for some reason until you click on them. 但是由于某些原因,这些字段显示为空白,直到您单击它们。 And the problem with some fields being wiped out in subsequent pages occurs like in the true false scenario (first scenario mentioned). 而且,在某些页面中擦除某些字段的问题会像在真假场景(提到的第一个场景)中一样发生。

I am wondering if it is possible to get this to work without misaligned field values, without flattening the fields, and without lost fields on subsequent pages. 我想知道是否有可能使它工作而不会导致字段值未对齐,不使字段变平并且在后续页面上没有丢失字段。

I know you can adjust margins afterwards using 我知道您以后可以使用调整边距

acroFields.setExtraMargin(extraMarginLeft, extraMarginTop)

but using 但是使用

acroFields.setGenerateAppearances(false)

works perfectly for a single form without having to adjust margins and I want it to work for a multi-page document as well. 非常适合单个表单,而无需调整页边距,我也希望它也适用于多页文档。

Also, using 另外,使用

acroFields.setGenerateAppearances(true)

causes the text to move and be displaced a little bit in the textbox when you click on it. 导致文本在单击时在文本框中移动和移位。 This happens for both single-page documents and multi-page documents. 单页文档和多页文档都会发生这种情况。 There seems to be a bug in either iText or PDF templates created with Adobe Pro when setting fields with setGenerateAppearances(true). 使用setGenerateAppearances(true)设置字段时,使用Adobe Pro创建的iText或PDF模板中似乎都存在错误。

I am currently using iText 5.5.8. 我当前正在使用iText 5.5.8。

Any help with this issue would be greatly appreciated. 任何与此问题的帮助将不胜感激。 Thanks for taking the time to read this. 感谢您抽时间阅读。

It's a very long question, and I guess that makes it difficult for people to answer it. 这是一个很长的问题,我想这使人们很难回答。 I don't have a conclusive answer either, because I can't reproduce the problem. 我也没有结论性的答案,因为我无法重现问题。 However, I can clarify a couple of things. 但是,我可以澄清两件事。

1. In PDF, one field can correspond with more than one widget annotation. 1.在PDF中,一个字段可以对应一个以上的小部件注释。 One field can have only one value. 一个字段只能有一个值。

Suppose that you have a PDF form with a field named "name". 假设您有一个PDF表单,其中包含一个名为“ name”的字段。 It is possible for that field to appear on different places in the document. 该字段可能出现在文档中的不同位置。 For instance: if a form has multiple pages, the field "name" could correspond with a widget annotation on every page (eg in the header). 例如:如果一个表单有多个页面,则字段“名称”可能与每个页面上的小部件注释相对应(例如,标题中)。

The field "name" can only have a single value, for instance: "Charles Carrington." 字段“名称”只能有一个值,例如:“ Charles Carrington”。 If the field corresponds with different widget annotations, then each of these visualizations should show the same name. 如果该字段与不同的窗口小部件注释相对应,则这些可视化中的每一个都应显示相同的名称。

It is impossible for the field "name" to have the name "Charles Carrington" on one page, and "Bruno Lowagie" on another other page. 字段“名称”不可能在一页上具有名称“ Charles Carrington”,而在另一页上具有“ Bruno Lowagie”。

Why is this important for you? 为什么这对您很重要?

You experimented with setFormFlattening() . 您尝试了setFormFlattening()

If you use this method with the value false , then you are doing a couple of things wrong: 如果您使用此方法的值为false ,那么您做错了几件事:

  1. You don't tell PdfSmartCopy that you are merging forms: How to merge forms from different files into one PDF? 您不会告诉PdfSmartCopy您正在合并表格: 如何将不同文件中的表格合并为一个PDF?
  2. You are violating the rule "1 field = 1 value" because you first fill out fields in different forms with different values (eg form 1: name = Charles Carrington; form 2: name = Bruno Lowagie), then you merge these forms into a form where you suddenly expect one field to have different values (eg mergedform: name = Charles Carrington on page 1; name = Bruno Lowagie on page 2). 您违反规则“ 1字段= 1值”,因为您首先用不同的值(例如表格1:名称= Charles Carrington;表格2:名称= Bruno Lowagie)以不同的形式填写了字段,然后将这些表格合并为一个您突然希望一个字段具有不同值的表单(例如,mergedform:第1页上的名称= Charles Carrington;第2页上的名称= Bruno Lowagie)。 This is in violation with ISO-32000-1. 这违反了ISO-32000-1。

You can avoid this problem by: 您可以通过以下方法避免此问题:

  • renaming the fields if it's important to you that the interactivity is preserved. 如果保留交互性对您很重要,则重命名字段。
  • flattening the fields: in this case, all fields are removed. 展平字段:在这种情况下,所有字段都将被删除。 Instead you add the appearance of the values. 而是添加值的外观。 All interactivity is lost. 所有交互都将丢失。

2. In PDF, a field has a value, but it can also have one or more appearances. 2.在PDF中,字段具有值,但也可以具有一个或多个外观。

Suppose that you have a PDF form with a field named "birthdate" The value of that field is "1970-06-10" (and that's also the way how it's stored in a database). 假设您有一个PDF表单,其中包含一个名为“ birthdate”的字段,该字段的值为“ 1970-06-10”(这也是它在数据库中的存储方式)。 However, when you fill out the field in a PDF document, you want it to show as "June 10, 1970". 但是,当您在PDF文档中填写该字段时,您希望它显示为“ 1970年6月10日”。

This is possible. 这个有可能。 The value of the /V key of the field dictionary will be the PDF string 1970-06-10 . 字段字典的/V键的值将是PDF字符串1970-06-10 However, the /DA key will define an appearance that shows "June 10, 1970". 但是, /DA键将定义一个显示为“ 1970年6月10日”的外观

It is even possible to have a field with a single value ( 1970-06-10 ) corresponding with different widget annotations that have a different appearance: "June 10, 1970", "10 juin 1970", "10 juni 1970", and so on. 甚至可能有一个具有单个值( 1970-06-10 )的字段,该字段对应于外观不同的不同窗口小部件注释:“ 1970年6月10日”,“ 10年6月1970年”,“ 10年6月1970年”和以此类推。

Why is this important for you? 为什么这对您很重要?

You have been experimenting with setGenerateAppearances() . 您一直在尝试setGenerateAppearances()

When you use this method with the value false , you instruct iText to omit the /DA : no appearance is created. 当使用值为false此方法时,将指示iText忽略/DA :不创建外观。 When you flatten the form, the fields are empty. 展平表格时,字段为空。 When you don't flatten the form, the PDF viewer will create the appearance. 当您不展平表格时,PDF查看器将创建外观。 Since Adobe and other vendors haven't always been consistent in the way they render PDF, it is very hard to predict what that appearance will look like. 由于Adobe和其他供应商的PDF呈现方式并不总是一致的,因此很难预测其外观。 One viewer will show the value at one position within the rectangle defined for the widget annotation; 一位查看者将在为小部件注释定义的矩形内的一个位置显示该值; another viewer will show it with a different offset. 另一个查看器将以不同的偏移量显示它。

When you use this method with the value true , you instruct iText to create the appearance in a consistent way. 当使用值为true此方法时,将指示iText以一致的方式创建外观。

However: if you don't flatten the form, you can have the effect described in my answer to the question Why does iText enter a cross symbol when CheckType style is check mark? 但是:如果不展平表格,则可以得到我对以下问题的回答中描述的效果:问题: 当CheckType样式为复选标记时,iText为什么输入十字符号? In this example, you see that the appearance of a check box depends on whether or not the field is high-lighted. 在此示例中,您会看到复选框的外观取决于该字段是否突出显示。 The same goes for text fields: the appearance can be different whether or not you select it. 文本字段也是如此:无论您是否选择,外观都可以不同。 Also: when you change the value, you get the appearance as created by the viewer. 另外:更改值时,将获得查看器创建的外观。 Eg when you click on "June 10, 1970" because you want to change it, you will suddenly see "1970-06-10" because that's the value that is stored for the field and that value is generated by the viewer. 例如,当您因为要更改而单击“ 1970年6月10日”时,会突然看到“ 1970-06-10”,因为这是为该字段存储的值,并且该值是由查看器生成的。

If you flatten the form, then iText creates the appearance and it removes all interactivity. 如果将表单展平,则iText将创建外观并删除所有交互性。 In this case, the viewer doesn't create any appearance: there are no more fields in the form. 在这种情况下,查看器不会创建任何外观:表单中没有其他字段。

3. iText always creates the flattened appearance in the same way. 3. iText始终以相同的方式创建展平外观。

This is the mystery that remains after reading your question. 这是阅读您的问题后仍然存在的奥秘。 You claim that the appearance when you fill and flatten a single form is different from the appearance when you fill and flatten many forms and then concatenate them. 您声称填充和展平单个表单时的外观与填充并展平许多表单然后将它们连接在一起时的外观不同。 I can't reproduce that problem. 我无法重现该问题。 The only answer I can give you to that question is: It works for me. 对于这个问题,我唯一能给您的答案是: 它对我有用。 (If you don't believe me, then please watch this tutorial .) (如果您不相信我,请观看本教程 。)

Please adapt your example based on the information given in 1. and 2. , then post a new, shorter question if the problem persists. 请根据1.2.中提供的信息调整您的示例,然后如果问题仍然存在,则发布一个新的简短问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM