Сглаживание формы с использованием PDFClown вызывает исключение IndexOutOfBounds

Я использую PDFClown-0.2.0, чтобы сгладить этот файл PDF. Вот код, который у меня есть:

import org.pdfclown.documents.Document;
import org.pdfclown.files.File;
import org.pdfclown.files.SerializationModeEnum;
import org.pdfclown.tools.FormFlattener;

public class Sample {
    public static void main(String args[]){
        try {
            File f = new File("label.pdf");
            Document doc = f.getDocument();

            FormFlattener formFlattener = new FormFlattener();
            formFlattener.flatten(doc);
            f.save(SerializationModeEnum.Standard);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Я следую инструкции, представленной на http://pdfclown.org/2014/09/12/waiting-for-pdf-clown-0-2-0-release/. Однако, когда я запускаю код, я получаю следующую ошибку:

java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.ArrayList.rangeCheck(ArrayList.java:653)
    at java.util.ArrayList.get(ArrayList.java:429)
    at org.pdfclown.objects.PdfArray.get(PdfArray.java:314)
    at org.pdfclown.documents.interaction.forms.FieldWidgets.get(FieldWidgets.java:135)
    at org.pdfclown.documents.interaction.forms.FieldWidgets$1.next(FieldWidgets.java:380)
    at org.pdfclown.documents.interaction.forms.FieldWidgets$1.next(FieldWidgets.java:1)
    at org.pdfclown.tools.FormFlattener.flatten(FormFlattener.java:74)
    at com.narvar.webservices.returns.retailers.Sample.main(Sample.java:18)

Что я делаю неправильно? Просто обратите внимание, что PDF был создан с использованием PDFBox, и я сделал поля формы только для чтения.

1 ответ

Решение

После отладки в коде это выглядит как ошибка PdfClown:

Iterator вернулся org.pdfclown.documents.interaction.forms.FieldWidgets.iterator() не распознает, что нижняя коллекция виджетов изменилась (стала меньше), и поэтому пытается читать за ее размер.

В деталях:

org.pdfclown.tools.FormFlattener.flatten(Document) перебирает виджеты поля:

  for(Widget widget : field.getWidgets())

но внутри этого цикла он удаляет текущий виджет из списка " Дети" текущего поля:

    // Removing the field references relating the widget...
    PdfDictionary fieldPartDictionary = widget.getBaseDataObject();
    while (fieldPartDictionary != null)
    {
      [...]
      kidsArray.remove(fieldPartDictionary.getReference());
      [...]
    }

Таким образом, коллекция, над которой внешний for повторяет изменения. К сожалению Iterator используется здесь не в курсе изменений в базовой коллекции

return new Iterator<Widget>()
{
  /** Index of the next item. */
  private int index = 0;
  /** Collection size. */
  private final int size = size();

  @Override
  public boolean hasNext( )
  {return (index < size);}

  @Override
  public Widget next( )
  {
    if(!hasNext()) throw new NoSuchElementException();
    return get(index++);
  }

  @Override
  public void remove( )
  {throw new UnsupportedOperationException();}
};

Поскольку вы видите, что он не просто не информирован и не проверяет саму базовую коллекцию, он даже имеет свое представление о размере коллекции, которая является размером коллекции в Iterator поколение в size,

Такой Iterator реализация подходит для неизменяемых коллекций, которые могут быть реализованы в соответствии с архитектурой или контрактом. Но в данном случае я не вижу ни одной, архитектура, очевидно, позволяет изменять коллекцию, и нет никаких намеков на то, что рассматриваемый итератор может использоваться только для стабильных базовых коллекций.

Это должно быть исправлено.

Подход к решению

Решение может быть предпринято путем изменения FormFlattener.flatten получить локальную копию виджетов и перебрать эту копию, например, заменив

  for(Widget widget : field.getWidgets())

с

  List<Widget> widgets = new ArrayList<Widget>(field.getWidgets());
  for(Widget widget : widgets)
Другие вопросы по тегам