iText7 Java добавляет проблему с текстом

Я использую iText 7 для дублирования страниц pdf и нумерации этих страниц. Так что мне не нужно нумеровать их вручную. Но есть проблема с числами в сгенерированном PDF-файле. Вот как это выглядит:

Изображение проблемы

И я думаю об этом много часов, но до сих пор не могу понять.

Мой код:

import com.itextpdf.io.font.FontConstants;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;

import java.io.File;
import java.io.IOException;

public class NumberingInJava {

public static final String SRC = "D:/temp/num_src.pdf";
public static final String DEST = "D:/temp/edited_numbering.pdf";

public static final String[] NUM4SAMPLE = {"02A", "03A", "03B", "03C", "04A", "08A"};

public static final double XCOOR = 230;
public static final double YCOOR = 795;//755

public static void main(String[] args) throws IOException {
    File file = new File(DEST);
    file.getParentFile().mkdirs();
    new NumberingInJava().manipulatePdf(SRC, DEST, NUM4SAMPLE);
}

private void manipulatePdf(String src, String dest, String[] numbering4what) throws IOException {

    //Initialize PDF document
    PdfDocument pdfDocToRead = new PdfDocument(new PdfReader(src));
    PdfDocument pdfDocToWrite = new PdfDocument(new PdfWriter(dest));

    for(String s : numbering4what) {
        println(s);
    }

    String number = null;
    PdfPage tempPage = null;
    for (int i=0; i<numbering4what.length; i++) {
        pdfDocToRead.copyPagesTo(1, 2, pdfDocToWrite);
        number = numbering4what[i];
        println(number);
        tempPage = pdfDocToWrite.getPage(2*(i+1)-1);

        numberingPage(tempPage, number);

        println("pdfDocToWrite.numberOfPages : "+pdfDocToWrite.getNumberOfPages());
    }

    pdfDocToRead.close();
    pdfDocToWrite.close();

    println("\nNumber added!");
}

private void numberingPage(PdfPage pdfPage, String number) throws IOException {
    println(pdfPage);
    PdfCanvas canvas = new PdfCanvas(pdfPage);
    canvas.beginText().setFontAndSize(PdfFontFactory.createFont(FontConstants.HELVETICA), 22)
            .moveText(XCOOR, YCOOR)
            .showText(number)
            .endText();

    println("number: "+number);

}

private void println(Object obj) {
    System.out.println(obj);
}
}

The console output:

02A
03A
03B
03C
04A
08A
02A
com.itextpdf.kernel.pdf.PdfPage@cf768c
17:58:07,457 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
17:58:07,458 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
17:58:07,458 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [jar:file:/D:/Java_Packages/ext_lib/iText/itext7-7.0.1/itext7-itext-rups-7.0.1.jar!/logback.xml]
17:58:07,459 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs multiple times on the classpath.
17:58:07,459 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/D:/Java_Packages/ext_lib/iText/itext7-7.0.1/itext7-itext-rups-7.0.1-jar-with-dependencies.jar!/logback.xml]
17:58:07,459 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/D:/Java_Packages/ext_lib/iText/itext7-7.0.1/itext7-itext-rups-7.0.1-sources.jar!/logback.xml]
17:58:07,459 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [jar:file:/D:/Java_Packages/ext_lib/iText/itext7-7.0.1/itext7-itext-rups-7.0.1.jar!/logback.xml]
17:58:07,501 |-INFO in ch.qos.logback.core.joran.spi.ConfigurationWatchList@8080bb - URL [jar:file:/D:/Java_Packages/ext_lib/iText/itext7-7.0.1/itext7-itext-rups-7.0.1.jar!/logback.xml] is not of type file
17:58:07,662 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
17:58:07,829 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [com.itextpdf.rups.view.DebugAppender]
17:58:07,851 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [DEFAULT_APP]
17:58:07,940 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
17:58:08,045 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [com.itextpdf.rups.view.StyleAppender]
17:58:08,046 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [INFO_APP]
17:58:08,062 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
17:58:08,063 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [com.itextpdf.rups.view.DebugAppender]
17:58:08,063 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [DEBUG_APP]
17:58:08,065 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
17:58:08,066 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [com.itextpdf.rups.view.DebugAppender]
17:58:08,066 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [TRACE_APP]
17:58:08,067 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
17:58:08,069 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [com.itextpdf.rups.view.StyleAppender]
17:58:08,069 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [IMPORTANT_APP]
17:58:08,075 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
17:58:08,076 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting additivity of logger [com.itextpdf] to false
17:58:08,077 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [IMPORTANT_APP] to Logger[com.itextpdf]
17:58:08,078 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [INFO_APP] to Logger[com.itextpdf]
17:58:08,078 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [DEBUG_APP] to Logger[com.itextpdf]
17:58:08,078 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [TRACE_APP] to Logger[com.itextpdf]
17:58:08,078 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to TRACE
17:58:08,078 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [DEFAULT_APP] to Logger[ROOT]
17:58:08,078 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
17:58:08,082 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@1c24521 - Registering current configuration as safe fallback point

номер: 02A pdfDocToWrite.numberOfPages: 2 03A com.itextpdf.kernel.pdf.PdfPage@11aa95a номер: 03A pdfDocToWrite.numberOfPages: 4 03B com.itextpdf.kernel.pdf.PdfPage@bc05adf номер pdf.PdfPage@bc05adf6 .itextpdf.kernel.pdf.PdfPage @ ef309d номер: 03C pdfDocToWrite.numberOfPages: 8 04A com.itextpdf.kernel.pdf.PdfPage@1fc609f номер: 04A pdfDocToWrite.numberOfPages: 10 08df17.df13axtpages.pdf номер: 08A pdfDocToWrite.numberOfPages: 12

Номер добавлен!

Процесс завершен с кодом выхода 0

РЕДАКТИРОВАТЬ: я загрузил смоделированный документ и отредактированный документ в Dropbox, вот он: Имитация документ

отредактированный имитационный документ


person user6839234    schedule 09.10.2016    source источник
comment
почему вы хотите дублировать страницы pdf для нумерации? похоже, ваше изображение перекрывается новым содержанием. Можете ли вы сказать, чего именно вы хотите достичь?   -  person Sasi Kathimanda    schedule 09.10.2016
comment
@SasiKathimanda Да, мне нужно скопировать документ и пронумеровать его вручную, чтобы передать в другой отдел раньше. Поэтому я сканирую документ в файл pdf, а затем использую этот код для автоматической нумерации документа. Это сэкономит много времени.   -  person user6839234    schedule 09.10.2016
comment
правильно, какова стратегия дублирования? Я имею в виду, что вы дублируете страницы, это зависит от количества страниц в файле src. можешь прикрепить файл src? а также опубликуйте вывод консоли.   -  person Sasi Kathimanda    schedule 09.10.2016
comment
@SasiKathimanda Я добавил к вопросу вывод консоли. Не знаю, как прикрепить файл src, к тому же он засекречен в компании. После того, как я отсканировал, в файле две страницы. Первая страница - это лицевая сторона моего документа, а вторая страница - обратная сторона. Я продублирую эти две страницы 6 раз, но мне нужно только добавить числа на лицевой стороне страницы (1, 3, 5, 7, 9, 11). После этого я могу выполнять двустороннюю печать и не нумеровать страницы вручную.   -  person user6839234    schedule 09.10.2016
comment
Если у вас есть очищенный файл (без секретной информации), в котором воспроизводится проблема, поместите файл в службу обмена файлами, например Dropbox, и поделитесь ссылкой здесь.   -  person Amedee Van Gasse    schedule 10.10.2016
comment
@AmedeeVanGasse Я загрузил документ в Dropbox Simulated doc & отредактировал смоделированный документ   -  person user6839234    schedule 10.10.2016


Ответы (1)


Причина

Проблема вызвана тем, что iText пытается уменьшить размер PDF-файла:

Когда вы копируете страницы pdfDocToRead несколько раз в pdfDocToWrite, фактический поток содержимого страницы, ресурсы страницы и т. Д. Копируются только один раз, просто небольшой объект, ссылающийся на эти данные, создается один раз для каждой копии и страницы.

Эта оптимизация пока не является проблемой, но дальнейшая микрооптимизация в

PdfCanvas canvas = new PdfCanvas(pdfPage);

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

private static PdfStream getPageStream(PdfPage page) {
    PdfStream stream = page.getContentStream(page.getContentStreamCount() - 1);
    return stream == null || stream.getOutputStream() == null || stream.containsKey(PdfName.Filter) ? page.newContentStreamAfter() : stream;
}

Как видите, обычно на страницу добавляется новый поток контента (page.newContentStreamAfter()); только если поток контента уже существует и у этого потока нет фильтра (например, для сжатия), этот существующий поток контента используется для добавления данных.

В случае вашего документа единственный поток контента, скопированный для каждой исходной страницы, не сжимается. Таким образом, эти две оптимизации приводят к тому, что все ваши PdfCanvas canvas экземпляры добавляются к одному и тому же потоку контента.

Обход

Очевидный обходной путь состоит в том, чтобы обойти последнюю оптимизацию: заменить строку

PdfCanvas canvas = new PdfCanvas(pdfPage);

by

PdfCanvas canvas = new PdfCanvas(pdfPage.newContentStreamAfter(), pdfPage.getResources(), pdfPage.getDocument());

StampPageNumbers.java метод numberingPage)

По сути, это то, что также произошло бы, если бы исходный поток контента был сжат.

(В общем, может действительно потребоваться добавить новый поток контента перед текущим с помощью инструкции save-graphics-state и сначала добавить инструкцию restore-graphics-state в новый поток контента после текущего; в случае вашего образца документа, хотя в этом нет необходимости, потому что текущее содержимое не изменяет состояние графики.)

person mkl    schedule 18.10.2016
comment
Спасибо, это очень помогло :) - person user6839234; 18.10.2016