iText7 Java添加文本问题

时间:2016-10-09 08:51:26

标签: java groovy itext itext7

我使用iText 7复制pdf页面并对这些页面进行编号。所以我不需要手动编号。但是生成的pdf文件中的数字存在问题。这是它的样子:

Issue image

我已经考虑了好几个小时,仍然无法弄清楚。

我的代码:

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);
}
}

控制台输出:      

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

number: 02A pdfDocToWrite.numberOfPages : 2 03A com.itextpdf.kernel.pdf.PdfPage@11aa95a number: 03A pdfDocToWrite.numberOfPages : 4 03B com.itextpdf.kernel.pdf.PdfPage@bc05a6 number: 03B pdfDocToWrite.numberOfPages : 6 03C com.itextpdf.kernel.pdf.PdfPage@ef309d number: 03C pdfDocToWrite.numberOfPages : 8 04A com.itextpdf.kernel.pdf.PdfPage@1fc609f number: 04A pdfDocToWrite.numberOfPages : 10 08A com.itextpdf.kernel.pdf.PdfPage@173813a number: 08A pdfDocToWrite.numberOfPages : 12

Number added!

Process finished with exit code 0

编辑: 我在Dropbox上传了一个模拟文档和一个编辑过的文档,这里是: Simulated doc

edited Simulated doc

1 个答案:

答案 0 :(得分:0)

原因

问题是因为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);

通过

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

(在StampPageNumbers.java方法numberingPage中)

如果原始内容流被压缩,这基本上也会发生。

(通常,实际上可能需要在当前使用save-graphics-state指令添加新的内容流,并且最初在当前之后向新内容流添加restore-graphics-state指令;如果是但是,样本文档不是必需的,因为当前内容不会对图形状态产生不利影响。)