使用较少的内存填充PDF表单,使用iText进行展平

时间:2012-01-05 19:53:55

标签: java itext

我有一个Web应用程序,它使用几个PDF表单来创建最多500页的文档;每个表单是一个页面,上面有40-50个字段。完成的文档仅显示和打印,在创建文档时无需保留PDF表单的填充方面。

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

该应用程序确实使用了大量内存,最近导致服务器崩溃。我感兴趣的是我是否可以修改现有代码以保留其大部分属性并使用更少的内存。在我看来,应该是可能的,因为使用的内存量表明整个文档在内存中直到完成,而我的逻辑没有必要 - 一旦页面填满,我的程序完成后,可以写入磁盘,并释放与该页面关联的任何内存。

我找到了com.lowagie.text.pdf.PdfWriter.freeReader()方法的参考,但我不确定如何在我的环境中使用它。我的问题是它是否会导致我的程序使用更少的内存(一次)以及拨打电话的位置。

我按如下方式创建iText Document,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);
}

'setSimpleColumn()'是一个完整的例程,用于处理将文本拟合到表单上的矩形中。

private int setSimpleColumn(ColumnText columnText, String value, Font fontToUse, 
                                Rectangle box, int leading, int alignment, boolean simulate)
    throws DocumentException
{
  columnText.setSimpleColumn(new Phrase(value, fontToUse),
        box.left(), box.bottom(),
        box.right(), box.top(),
       leading, alignment
      );
  int result = columnText.go(simulate);
  return result;
}

同样,主要的两个问题是:使用PdfWriter.freeReader()帮助释放在文档完成之前保留的内存,以及(2)我将在何处调用它?

如果有人想告诉我如何制作多页表格,我也对此感兴趣......

2 个答案:

答案 0 :(得分:4)

以下三个步骤对我有用:

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

Merging 1000 PDF thru iText throws java.lang.OutOfMemoryError: Java heap space

其中介绍了如何使用PdfWriter的freeMemory()方法。

  • 其次,您可以使用 RandomAccessFileOrArray

    阅读pdf来节省内存
    PdfReader pdfReader = new PdfReader(new RandomAccessFileOrArray(pdf), null);
    

而不是

PdfReader pdfReader = new PdfReader(pdf);
  • 最后,你可以System.gc()解雇java的自动垃圾收集工具。

答案 1 :(得分:1)

我没有看到循环遍历文档的代码,但是当您连接多个文档时,PdfWriter.freeReader()将释放内存。这是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个表单,执行相同操作然后组合/将最终文档与这些其他创建文档合并,以避免一切都打开直到结束?