itext7 объединяет документы, созданные HtmlConverter.convertToDocument, и сохраняет контуры - PullRequest
0 голосов
/ 28 января 2019

Мои два документа создаются с использованием HtmlConverter.convertToDocument и впоследствии объединяются в один PDF:

PdfDocument pdf = new PdfDocument(new PdfWriter(pdfDest));

PdfMerger merger = new PdfMerger(pdf, false, true).setCloseSourceDocuments(true);

// Convert
ConverterProperties converterProperties = new ConverterProperties().setBaseUri(resourceFolder);
OutlineHandler outlineHandler = OutlineHandler.createStandardHandler();
converterProperties.setBaseUri(".");
converterProperties.setOutlineHandler(outlineHandler);

Первый документ содержит закладки «HTML Ipsum Presents», а второй - «Plastic_parts_Basic» и «Amo» (с детьми).

Обратите внимание на использование обработчика контура.После слияния кажется, что закладки смешиваются.Это имеет смысл, учитывая, что OutlineHandler для каждого документа создает места назначения по одному и тому же шаблону:

OutlineHandler addOutline(ITagWorker tagWorker, IElementNode element, ProcessorContext context) {
    String tagName = element.name();
    if (null != tagWorker && hasTagPriorityMapping(tagName) && context.getPdfDocument() != null) {
        int level = (int) getTagPriorityMapping(tagName);
        if (null == currentOutline) {
            currentOutline = context.getPdfDocument().getOutlines(false);
        }
        PdfOutline parent = currentOutline;
        while (!levelsInProcess.isEmpty() && level <= levelsInProcess.getFirst()) {
            parent = parent.getParent();
            levelsInProcess.pop();
        }
        String content = ((JsoupElementNode) element).text();
        if (content.isEmpty()) {
            content = getUniqueID(tagName);
        }
        PdfOutline outline = parent.addOutline(content);
        String destination = DESTINATION_PREFIX + getUniqueID(DESTINATION_PREFIX);
        outline.addDestination(PdfDestination.makeDestination(new PdfString(destination)));

        destinationsInProcess.push(destination);

        levelsInProcess.push(level);
        currentOutline = outline;
    }
    return this;
}

Нажатие на «Уровень заголовка 2» в закладках будет указывать на второй заголовок в последнем объединенном документе («Amo»)."):

enter image description here

Я пытался расширить класс OutlineHandler, но метод, который мне нужно изменить (getUniqueID), является закрытым и поэтому невидимым всуперкласс.

Есть ли способ получить уникальные места назначения для нескольких документов, созданных из HTML?

Исходные файлы (java и html) и полученные PDF-файлы (см. RFQMerge.pdf) находятся здесь: исходный код, файлы и результат

Принятый ответ у меня не сработал, я продолжал получать NullPointerException во второй строке этого кода:

PdfDictionary names = targetPdf.getCatalog().getPdfObject().getAsDictionary(PdfName.Names); names.put(PdfName.Dests, replaceDict);

Вот код курса и файлы ввода / исходного кода: https://www.dropbox.com/s/kg7vsb0j3hbkfca/stackoverflowClarification.zip?dl=0

Ответы [ 2 ]

0 голосов
/ 23 июля 2019

Спасибо, Владимир Асипчук, за ваш ответ, он сработал для меня.

Я немного изменился в соответствии с моим требованием, мое требование - объединить два или более PDF-файлов, которые могут быть созданы из HTML или уже созданных PDF-файлов или из обоих.

Я столкнулся с некоторой проблемой при вызовеметод rebuild ()

PdfDictionary replaceDict = newNameTree.buildTree();
replaceDict.makeIndirect(updateDestNamesDocument);

com.itextpdf.kernel.PdfException: не существует связанного PdfWriter для создания indire cts.

Итак, я только что добавил егокак новая запись с пользовательским именем и обновленная так же на контурах.Он работает

try {
    String prefix = "cus-" + (index) + "-";
    PdfNameTree destsTree = pdf.getCatalog().getNameTree(PdfName.Dests);
    PdfNameTree newNameTree = new PdfNameTree(pdf.getCatalog(), PdfName.Dests);
    for (Map.Entry<String, PdfObject> entry : destsTree.getNames().entrySet()) {
        newNameTree.addEntry(prefix + entry.getKey(), entry.getValue());
    }

    for (Map.Entry<String, PdfObject> entry : newNameTree.getNames().entrySet()) {
        destsTree.addEntry(prefix + entry.getKey(), entry.getValue());
        System.out.println(entry.getKey() +"==>>"+ entry.getValue());
    }


    PdfOutline rootOutline = pdf.getOutlines(false);
    updateOutlines(rootOutline, prefix);
} catch (Exception e) {
    e.printStackTrace();
}

Отображение вновь добавленного места назначения в контуры

public static void updateOutlines(PdfOutline parentOutline, String prefix) {
    for (PdfOutline outline : parentOutline.getAllChildren()) {
        updateOutlines(outline, prefix);
    }
    if (parentOutline.getDestination() instanceof PdfStringDestination) {
        parentOutline.addDestination(new PdfStringDestination(prefix + ((PdfString) parentOutline.getDestination().getPdfObject()).getValue()));
    }
}

Это работает правильно для меня, сделайте объединение после этих изменений, чтобы все ваши места назначения выглядели уникальными, и это не будетполучить переопределение при слиянии.

0 голосов
/ 29 января 2019

Ваша проблема заключается в следующем: iText генерирует pdf с теми же именами контуров и не разрешает их во время слияния (вместо этого iText регистрирует предупреждение и заменяет старый пункт назначения новым).

Естьдва способа обработки описанной ситуации:

1) Создание PDF-файлов с уникальными именами схем.К сожалению, в настоящее время реализация OutlineHandler является слишком закрытой, и это невозможно переопределить должным образом.Однако вы можете создать собственную версию pdfHTML для ваших нужд.Репо находится по адресу https://github.com/itext/i7j-pdfhtml, и вас интересует метод сброса OutlineHandler:

 /**
 * Resets the current state so that this {@link OutlineHandler} is ready to process new document
 */
public void reset() {
    currentOutline = null;
    destinationsInProcess.clear();
    levelsInProcess.clear();
    uniqueIDs.clear();
}

Просто прокомментируйте его последнюю строку и постройте банку.

2) Переименуйтепункты назначения документа, если вы знаете, что они вызывают некоторые проблемы.Даже если PdfMerger просто заменить старый пункт назначения новым, в нем записывается предупреждение об этом.Вы можете получить имена пунктов назначения, которые были переопределены, и переименовать их вручную перед слиянием.

Для этого необходимо: a) обновить имена пунктов назначения:

    PdfNameTree destsTree = updateDestNamesDocument.getCatalog().getNameTree(PdfName.Dests);
    PdfNameTree newNameTree = new PdfNameTree(updateDestNamesDocument.getCatalog(), PdfName.Dests);
    for (Map.Entry<String, PdfObject> entry : destsTree.getNames().entrySet()) {
        newNameTree.addEntry(prefix + entry.getKey(), entry.getValue());
    }
    PdfDictionary replaceDict = newNameTree.buildTree();
    replaceDict.makeIndirect(updateDestNamesDocument);

    PdfDictionary names = updateDestNamesDocument.getCatalog().getPdfObject().getAsDictionary(PdfName.Names);
    names.put(PdfName.Dests, replaceDict);

б) обновить контуры:

    PdfOutline rootOutline = updateDestNamesDocument.getOutlines(false);
    updateOutlines(rootOutline, prefix);

    private void updateOutlines(PdfOutline parentOutline, String prefix) {
    for (PdfOutline outline : parentOutline.getAllChildren()) {
        updateOutlines(outline, prefix);
    }
    if (parentOutline.getDestination() instanceof PdfStringDestination) {
        parentOutline.addDestination(new PdfStringDestination(prefix + ((PdfString)parentOutline.getDestination().getPdfObject()).getValue()));
    }
}

И тогда вы сможете успешно объединить свои PDF-файлы.

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