“随机”生成的文档无法打印

时间:2014-05-14 14:36:40

标签: java pdf printing itext printers

我们正在尝试使用iText 生成文档,这些文档主要来自"模板"文件 - 使用PdfContentByte.addTemplate方法将较小的PDF文件合并为一个复合文件。我们然后使用* nix命令lp自动并静默地打印新文件。这个通常有效;但偶尔会生成某些文件无法打印。文件继续通过所有队列并到达打印机本身(在这种情况下为Lexmark T652n),其物理显示屏显示待处理的进度信息,甚至其机械组件准备好 - 然后,打印作业自动消失跟踪,打印机返回准备状态。

这个问题具体如何具体的奇怪之处。对于初学者来说,当通过Adobe PDF Viewer手动完成时,有问题的文件会被打印出来,并且可以被Adobe Live Cycle等编辑所读取。此外,文件的内容会影响它是否受到此问题的困扰,但不是很清楚 - 添加特定模板20次可能会导致问题,而执行19或21次可能会导致问题罚款,或使用不同的模板将完全改变模式,并可能导致它发生37次后。生成具有完全相同内容的文档将是否一致是否出现问题,但内容中任何微妙且看似无关的更改都将改变问题是否发生。< / p>

虽然它可能被视为硬件问题,但事实仍然是某些iText生成的文件存在此问题,而其他文件则没有。 我们的文件创建方法是否有时会创建仅在某种程度上被认为仅对打印机损坏的文件?有时只会这样做?

这是一个相对较小的代码示例,它使用类似于我们主程序的重复模板方法生成文档。它使用this file作为模板并重复指定的次数。

public class PDFFileMaker {

    private static final int INCH = 72;

    final private static float MARGIN_TOP = INCH / 4;
    final private static float MARGIN_BOTTOM = INCH / 2;

    private static final String DIREC = "/pdftest/";
    private static final String OUTPUT_FILEPATH = DIREC + "cooldoc_%d.pdf";
    private static final String TEMPLATE1_FILEPATH = DIREC + "template1.pdf";
    private static final Rectangle PAGE_SIZE = PageSize.LETTER;
    private static final Rectangle TEMPLATE_SIZE = PageSize.LETTER;

    private ByteArrayOutputStream workingBuffer;
    private ByteArrayOutputStream storageBuffer;
    private ByteArrayOutputStream templateBuffer;

    private float currPosition;
    private int currPage;
    private int formFillCount;
    private int templateTotal;

    private static final int DEFAULT_NUMBER_OF_TIMES = 23;

    public static void main (String [] args) {

        System.out.println("Starting...");

        PDFFileMaker maker = new PDFFileMaker();

        File file = null;
        try {
            file = maker.createPDF(DEFAULT_NUMBER_OF_TIMES);
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        if (file == null || !file.exists()) {
            System.out.println("File failed to be created.");
        }
        else {
            System.out.println("File creation successful.");
        }
    }

    public File createPDF(int inCount) throws Exception {

        templateTotal = inCount;

        String sFilepath = String.format(OUTPUT_FILEPATH, templateTotal);

        workingBuffer = new ByteArrayOutputStream();
        storageBuffer = new ByteArrayOutputStream();
        templateBuffer = new ByteArrayOutputStream();

        startPDF();
        doMainSegment();
        finishPDF(sFilepath);

        return new File(sFilepath);
    }

    private void startPDF() throws DocumentException, FileNotFoundException {

        Document d = new Document(PAGE_SIZE);
        PdfWriter w = PdfWriter.getInstance(d, workingBuffer);
        d.open();
        d.add(new Paragraph(" "));
        d.close();
        w.close();

        currPosition = 0;
        currPage = 1;
        formFillCount = 1;
    }

    protected void finishPDF(String sFilepath) throws DocumentException, IOException {
        //Transfers data from buffer 1 to builder file
        PdfReader r = new PdfReader(workingBuffer.toByteArray());
        PdfStamper s = new PdfStamper(r, new FileOutputStream(sFilepath));
        s.setFullCompression();
        r.close();
        s.close();
    }

    private void doMainSegment() throws FileNotFoundException, IOException, DocumentException {
        File fTemplate1 = new File(TEMPLATE1_FILEPATH);
        for (int i = 0; i < templateTotal; i++) {
            doTemplate(fTemplate1);
        }
    }

    private void doTemplate(File f) throws FileNotFoundException, IOException, DocumentException {

        PdfReader reader = new PdfReader(new FileInputStream(f));

        //Transfers data from the template input file to temporary buffer
        PdfStamper stamper = new PdfStamper(reader, templateBuffer);
        stamper.setFormFlattening(true);

        AcroFields form = stamper.getAcroFields();

        //Get size of template file via looking for "end" Acrofield
        float[] area = form.getFieldPositions("end");
        float size = TEMPLATE_SIZE.getHeight() - MARGIN_TOP - area[4];

        //Requires Page Break
        if (size >= PAGE_SIZE.getHeight() - MARGIN_TOP - MARGIN_BOTTOM + currPosition) {

            PdfReader subreader = new PdfReader(workingBuffer.toByteArray());
            PdfStamper substamper = new PdfStamper(subreader, storageBuffer);

            currPosition = 0;
            currPage++;

            substamper.insertPage(currPage, PAGE_SIZE);

            substamper.close();
            subreader.close();

            workingBuffer = storageBuffer;
            storageBuffer = new ByteArrayOutputStream();
        }

        //Set Fields
        form.setField("field1", String.format("Form Text %d", formFillCount));
        form.setField("page", String.format("Page %d", currPage));
        formFillCount++;

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

        //Read from working buffer, stamp to storage buffer, stamp template from template buffer
        reader = new PdfReader(workingBuffer.toByteArray());
        stamper = new PdfStamper(reader, storageBuffer);
        reader.close();
        reader = new PdfReader(templateBuffer.toByteArray());

        PdfImportedPage page = stamper.getImportedPage(reader, 1);
        PdfContentByte cb = stamper.getOverContent(currPage);
        cb.addTemplate(page, 0, currPosition);

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

        //Reset buffers - working buffer takes on storage buffer data, storage and template buffers clear
        workingBuffer = storageBuffer;
        storageBuffer = new ByteArrayOutputStream();
        templateBuffer = new ByteArrayOutputStream();

        currPosition -= size;
    }

使用 DEFAULT_NUMBER_OF_TIMES为23 运行此程序会产生this document,并在发送到打印机时导致失败。将其更改为 22次会产生this similar-looking document(只需少一个&#34;行&#34;)没有问题且成功打印。使用不同的PDF文件作为模板组件会完全更改这些数字或使其完全不会发生。

虽然这个问题可能过于具体,并且有太多因素可供其他人合理地预期复制,但可能性的问题仍然存在。 文件生成怎么可能导致这种异常行为?什么可能导致一个文件被特定打印机接受,而另一个文件以相同的方式在不同的情况下以看似非平凡的方式生成不能接受的?通过使用压模模板命令生成的iText中是否存在错误?这对我们来说已经是一个长期存在的错误,所以任何帮助都是值得欣赏的;此外,我愿意在必要时回答问题或在聊天中进行扩展对话,以便深入了解。

1 个答案:

答案 0 :(得分:1)

您的应用程序的设计或多或少会滥用完全正常的PdfStamper功能。

请允许我解释一下。

页面的内容可以表示为流对象或流对象的数组。使用PdfStamper更改页面时,此页面的内容始终是一个流对象数组,由原始流对象或添加了额外元素的原始流对象数组组成。

通过添加相同的模板一遍又一遍地创建PdfStamper对象,可以显着增加页面内容数组中的元素数量。您还介绍了大量保存和恢复堆栈的qQ运算符。您有随机行为的原因很明显:处理PDF的内存和CPU可能因时而异。有一次,将有足够的资源来处理20个q运营商(保存状态),下次只有足够的资源来处理19.问题发生在流程资源耗尽时。 / p>

虽然根据ISO-32000-1,您创建的PDF不是非法的,但某些PDF处理器只会阻塞这些PDF。 iText是一个工具箱,允许您创建PDF,当我深入了解时,它可以让我非常高兴,但如果您不明智地使用工具箱,它还允许您创建可怕的PDF。后者就是你的情况。

您应该解决此问题,重复使用PdfStamper实例,而不是一遍又一遍地创建新的PdfStamper。如果那是不可能的,请发布另一个问题,使用较少的单词,准确解释你想要达到的目标。

假设您有许多不同的源文件,其中包含需要添加到单个页面的PDF片段。例如:假设每个PDF片段都是优惠券,您需要创建一张包含30张优惠券的工作表。与使用单个PdfWriter实例相比,使用getImportedPage()导入网页并使用addTemplate()将其添加到正确的位置。

当然:我不知道你的项目是什么。页面优惠券的想法受到了测试PDF的启发。