Использование меньшего объема памяти при заполнении PDF-формы, с выравниванием, используя iText - PullRequest
3 голосов
/ 05 января 2012

У меня есть веб-приложение, которое использует пару форм 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 ]

4 голосов
/ 16 октября 2012

Вот три следующих шага, которые сработали для меня:

  • Освобождение памяти, занятой писателем.Пожалуйста, обратитесь к этой ссылке.

Объединение 1000 PDF через iText бросает java.lang.OutOfMemoryError: Пространство кучи Java

Что объясняет, как использовать PdfWriter's freeMemory() method.

  • Во-вторых, вы можете сэкономить память, читая PDF, используя RandomAccessFileOrArray

    PdfReader pdfReader = new PdfReader(new RandomAccessFileOrArray(pdf), null);
    

вместо

PdfReader pdfReader = new PdfReader(pdf);
  • Наконец, вы можете System.gc() запустить утилиту автоматической сборки мусора Java.
1 голос
/ 10 января 2012

Я не вижу код, который проходит по документам, но 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 форм, сделайте то же самое и затем объедините / объединить окончательный документ с этими другими документами создания, чтобы не оставлять все открытым до конца?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...