Добавить именованные места назначения в существующий документ PDF с помощью iText

У меня есть PDF, ранее созданный с помощью FOP, и мне нужно добавить к нему несколько именованных мест назначения, чтобы позже другая программа могла открывать документ и перемещаться по нему с помощью параметров открытия Adobe PDF, а именно параметра #namedest=destination_name.

Мне не нужно добавлять закладки или другой динамический контент, а только некоторые места назначения с именем и, таким образом, вводить коллекцию /Dests с именами, определенными в итоговом PDF.

Я использую iText 5.3.0 и читаю главу 7 iText в действии (2-е издание), но все же не могу понять, как добавить места назначения и использовать их с #nameddest в браузере.

Я читаю и манипулирую документом с помощью PdfReader и PdfStamper. Я уже заранее знаю, куда поместить каждый пункт назначения после того, как проанализировал документ с помощью настроенного прослушивателя и PdfContentStreamProcessor, выполнив поиск определенного текстового маркера на каждой странице.

Это сокращенная версия моего кода:

PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new BufferedOutputStream(dest));

// search text markers for destinations, page by page
for (int i=1; i<reader.getNumberOfPages(); i++) {
  // get a list of markers for this page, as obtained with a custom Listener and a PdfContentStreamProcessor
  List<MyDestMarker> markers = ((MyListener)listener).getMarkersForThisPage();

  // add a destination for every text marker in the current page
  Iterator<MyDestMarker> it = markers.iterator();
  while(it.hasNext()) {
    MyDestMarker marker = it.next();
    String name = marker.getName();
    String x = marker.getX();
    String y = marker.getY();

    // create a new destination
    PdfDestination dest = new PdfDestination(PdfDestination.FITH, y); // or XYZ

    // add as a named destination -> does not work, only for new documents?
    stamper.getWriter().addNamedDestination(name, i /* current page */, dest);

    // alternatives
    PdfContentByte content = stamper.getOverContent(i);
    content.localDestination(name, dest); // doesn't work either -> no named dest found

    // add dest name to a list for later use with Pdf Open Parameters
    destinations.add(name);
  }   
}

stamper.close();
reader.close();

Я также попытался создать PdfAnnotation с помощью PdfFormField.createLink(), но, тем не менее, мне просто удается получить аннотацию, но без указания имени назначения она не работает.

Любое решение для этого? Нужно ли добавить какой-нибудь "призрачный" контент поверх существующего с помощью чанков или чего-то еще?

Заранее спасибо.


edit 27-27-2016: я недавно нашел ответ на свой вопрос в разделе примеров на сайте iText, здесь.

К сожалению, приведенный пример не работает для меня, если я протестирую его с pdf без мест назначения, ранее определенных в нем, как это имеет место с исходным файлом primes.pdf, который уже содержит массив /Dests. Такое поведение согласуется с кодом iText, поскольку средство записи загружает места назначения в атрибут карты PdfDocument, который не "наследуется" штампом при закрытии.

Тем не менее, я получил его с помощью метода addNamedDestination() из PdfStamper, добавленного в версии 5.5.7; этот метод загружает именованный пункт назначения в атрибут локальной карты класса, который позже обрабатывается и консолидируется в документе при закрытии штампа.

Однако при таком подходе возникла новая проблема: навигация с открытыми параметрами Pdf (#, # nameddest =) прекрасно работает с IE, но не с Chrome v47 (и, вероятно, Firefox тоже). Я проследил проблему до порядка, в котором имена десстов определены и указаны внутри документа; штамп использует HashMap в качестве контейнера для пунктов назначения, что, конечно, не гарантирует порядок его объектов, и по любой причине Chrome отказывается распознавать пункты назначения, не перечисленные в "естественном" порядке. Таким образом, единственный способ заставить его работать - это заменить namedDestination HashMap естественным образом упорядоченным TreeMap.

Надеюсь, что это поможет другим с той же проблемой.

1 ответ

Я был в той же потребности в моем проекте ранее. Пришлось отображать и перемещаться по документу PDF с помощью средства просмотра acrobat.jar. Для навигации мне понадобились названные направления в формате PDF. Я искал в Интернете возможное решение, но мне не повезло. Тогда мне эта идея приходит в голову.

Я попытался воссоздать существующий pdf с itext, перемещаясь по каждой странице и добавляя локальные назначения для каждой страницы, и я получил то, что хотел. ниже фрагмент моего кода

OutputStream outputStream = new FileOutputStream(new File(filename));
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte cb = writer.getDirectContent();
PdfOutline pol = cb.getRootOutline();
PdfOutline oline1 = null;
InputStream in1 = new FileInputStream(new File(inf1));
PdfReader reader = new PdfReader(in1);
for (int i = 1; i <= reader.getNumberOfPages(); i++)
{
    document.newPage();
    document.setMargins(0.0F, 18.0F, 18.0F, 18.0F);
    PdfImportedPage page = writer.getImportedPage(reader, i);
    document.add(new Chunk(new Integer(i).toString()).setLocalDestination(new Integer(i).toString()));
    System.out.println(i);
    cb.addTemplate(page, 0.0F, 0.0F);
}
outputStream.flush();
document.close();
outputStream.close();

Думала, это поможет тебе.

Другие вопросы по тегам