Java 使用更少的内存填充PDF表单,使用iText进行扁平化

Java 使用更少的内存填充PDF表单,使用iText进行扁平化,java,itext,Java,Itext,我有一个web应用程序,它使用几个PDF表单创建多达500页的文档;每个表单一页,有40-50个字段。已完成的文档仅用于显示和打印,在创建文档时,不需要保留PDF表单的填写功能 我有使用iText 1.4.5的工作代码;它在不到30秒的时间内创建了这些文档(websphere,MVS),这对我来说很好 该应用程序确实占用了大量内存,并且最近导致了服务器崩溃。我感兴趣的是,我是否可以修改现有代码,以保留其大部分属性,并使用更少的内存。在我看来,这应该是可能的,因为使用的内存量表明整个文档在完成之前

我有一个web应用程序,它使用几个PDF表单创建多达500页的文档;每个表单一页,有40-50个字段。已完成的文档仅用于显示和打印,在创建文档时,不需要保留PDF表单的填写功能

我有使用iText 1.4.5的工作代码;它在不到30秒的时间内创建了这些文档(websphere,MVS),这对我来说很好

该应用程序确实占用了大量内存,并且最近导致了服务器崩溃。我感兴趣的是,我是否可以修改现有代码,以保留其大部分属性,并使用更少的内存。在我看来,这应该是可能的,因为使用的内存量表明整个文档在完成之前都在内存中,而我的逻辑不需要这样做——一旦一个页面被填满,我的程序就完成了,它就可以被写入磁盘,任何与该页面相关的内存都可以被释放

我找到了对com.lowagie.text.pdf.PdfWriter.freeReader()方法的引用,但不确定如何在我的环境中使用它。我的问题是,这是否会导致我的程序使用更少的内存(一次)以及将调用放在何处

我创建iText文档、PdfWriter和PdfReader对象,如下所示:

public PdfFormFiller(String givenInputSpecification, 
                        Document givenDocument, 
                        PdfWriter givenWriter) 
{
  // instance fields stored for PDF or tracking purposes.
  inputSpecification = givenInputSpecification;
  document = givenDocument;
  writer = givenWriter;
  contentByte = writer.getDirectContent();
  // 'DirectContentUnder' is a contentByte object that allows
  // our app to write out document content that appears
  // underneath things written to the DirectContentOver; i.e.,
  // this is a layer underneath some other things.
  underContent = writer.getDirectContentUnder();

  try
  {
    PdfReader reader = new PdfReader(inputSpecification);
    template = writer.getImportedPage(reader, 1);           // this line limits us to 1-page forms;
    AcroFields aFields = reader.getAcroFields();            // the fields on the form.
  <<more stuff in this constructor, deleted from here>>  
我用以下公式向字段写入一个值:

/**
 * * 'Fill' this given form with the given data values, i.e., write the given data
 * values onto the positions in the forms corresponding to their field names. 
 * @param fieldValueMap a map with each key the name
 * of the data field, and each value the string to be put on
 * the form for that field.  
 */
public void fillForm(Map fieldValueMap) throws DocumentException
{
  Iterator keys = fieldValueMap.keySet().iterator();
  while (keys.hasNext())
  {
    String fieldName = (String)keys.next();
    FormField formField = (FormField)fields.get(fieldName);
    String value = null;
    if (fieldName != null)
      {
        value = (String)fieldValueMap.get(fieldName);
      }
    if (null != value && null != formField)
    {
      fillField(formField, value);
    }
  }
  // add the template of the form; the fact that it is added
  // to "underContent" causes iText to put it in a list if it's
  // not already there, so it only gets added once per doc.
  underContent.addTemplate(getTemplate(), 0, 0);

  // start a new page - throws DocumentException
  document.newPage();
}
/**
 * fills the given field with the given value
 * @param formField field and attributes
 * @param value String value
 */
private void fillField(FormField formField, String value) throws DocumentException
{
  if (formField.fieldType == AcroFields.FIELD_TYPE_CHECKBOX)
  {
    if (value.substring(0,1).equalsIgnoreCase("Y")) { value = "X"; } 
                                                else { value = " "; }
  }

  ColumnText columnText = new ColumnText(contentByte); 

  <<excised code determining fontToUse>>

        setSimpleColumn(columnText, value, fontToUse, formField.box,
                            leading, Element.ALIGN_LEFT, false);
}
同样,主要的两个问题是:使用PdfWriter.freeReader()是否有助于释放在文档完成之前一直保留的内存,以及(2)在哪里调用它


如果有人想告诉我如何制作多页表单,我也很感兴趣…

我没有看到在文档中循环的代码,但是当连接多个文档时,PdfWriter.freerereader()会释放内存。以下是javadoc的解释:

使用此方法将读取器写入文档并释放其使用的内存。主要用途是连接多个文档以将内存使用限制在当前附加文档中。

那你就是这么做的

听起来很简单,我认为您需要的是在循环处理过程中关闭每个文档,例如:

        //loop iteration
        // step 1
        Document document = new Document();
        // step 2
        PdfWriter.getInstance(document, new FileOutputStream(filename));
        // step 3
        document.open();
        // step 4
        document.add(new Paragraph("Hello World!"));
        //process the document.
        ...
        //save the document.
        ...
        // step 5
        document.close();
        //next loop iteration

既然您不需要保存每个文档,那么一次将20或30个表单合并为一个pdf,将其关闭,然后再创建20或30个表单是否可行,执行同样的操作,然后将最终文档与这些其他创建文档合并/合并,以避免将所有内容保留到最后?

以下三个步骤对我有效:

  • 释放作者占用的内存。请参考此链接

这说明了如何使用PdfWriter的
freemory()
方法

  • 其次,您可以通过使用随机访问文件阵列读取pdf来节省内存

    PdfReader pdfReader = new PdfReader(new RandomAccessFileOrArray(pdf), null);
    
而不是

  • 最后,您可以
    System.gc()
    启动java的自动垃圾收集实用程序

这正是我正在做的:通过多次调用fillForm创建一个文档。最后我得到了一个长PDF,它是表单的串联,由这个例程填充。我想在一切完成之前我无法关闭它。现在我已经阅读了您给我的javadoc代码片段,我想知道是否可以在document.newPage()之前调用write.freerereader()。只要我能在我们现有的环境中设置一个内存监视器,我就会尝试这样做。我会让你知道的。在这个例行程序中,我被javadoc愚弄了;我在PdfCopy中找到了它,但没有注意到它是对PdfWriter的重写。iText是一个很好的工具,但是很容易忽略api中的某些内容。我与它共事多年,现在仍在学习它;我认为这可能就是答案,尽管验证它需要付出比我花时间更多的努力(而且比我想象的要多得多)。
PdfReader pdfReader = new PdfReader(new RandomAccessFileOrArray(pdf), null);
PdfReader pdfReader = new PdfReader(pdf);