我们正在尝试使用主要由 "template"文件 形成的 iText 生成文档 - 使用 PdfContentByte.addTemplate
方法将较小的 PDF 文件组合成一个复合文件.我们然后使用 *nix 命令 lp
自动且静默地打印新文件。这通常有效;但是偶尔,生成的一些文件将打印失败。文档通过所有队列并到达打印机本身(在本例中为 Lexmark T652n),它的物理显示器给出了待处理进度的消息,甚至它的机械部件也开始准备 - 然后,打印作业自发消失而没有一个痕迹,打印机恢复准备就绪。
这个问题的具体程度很奇怪。对于初学者来说,当通过 Adobe PDF Viewer 手动完成时,有问题的文件打印不会失败,并且可以被 Adobe Live Cycle 等编辑器很好地阅读。此外,文件的内容影响它是否受到此问题的困扰,但不是以明确的方式 - 添加特定模板 20 次可能会导致问题,而添加 19 或 21 次可能是很好,或者使用不同的模板将完全改变模式,并可能导致它在 37 次后发生。生成具有完全相同内容的文档将在问题是否发生方面保持一致,但任何细微且看似无关的内容更改都会改变问题是否发生。
虽然这可能被认为是硬件问题,但事实仍然是某些 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 次strong> 会产生 this similar-looking document(只是少了一个“行”),这没有问题并且打印成功。使用不同的 PDF 文件作为模板组件会完全改变这些数字或使其根本不会发生。
虽然这个问题可能过于具体,并且有太多因素无法合理预期其他人会重现,但可能性问题仍然存在。 文件生成会导致这种异常行为吗?什么可能导致一个文件被特定打印机接受,而另一个以相同的方式生成,只是以看似不平凡的方式不同,却被接受不能接受? iText 中是否存在因过度使用压模模板命令而产生的错误?一段时间以来,这一直是我们的一个长期存在的错误,因此非常感谢您的帮助;此外,我愿意在必要时回答问题或在聊天中进行扩展对话,以深入了解此事。
最佳答案
您的应用程序的设计或多或少滥用了原本非常好的 PdfStamper
功能。
请允许我解释一下。
页面的内容可以表示为流对象或流对象数组。使用 PdfStamper
更改页面时,此页面的内容始终是一个流对象数组,由原始流对象或添加了额外元素的原始流对象数组组成。
通过添加相同的模板,一遍又一遍地创建一个 PdfStamper
对象,您可以显着增加页面内容数组中的元素数量。您还引入了大量用于保存和恢复堆栈的 q
和 Q
运算符。您具有随机行为 的原因很清楚:可用于处理 PDF 的内存和 CPU 可能因时刻而异。上一次会有足够的资源来处理20个q
算子(保存状态),下一次就只有足够的资源来处理19个了。问题出现在进程用完资源。
虽然根据 ISO-32000-1,您创建的 PDF 并非非法,但某些 PDF 处理器只会在处理这些 PDF 时出现问题。 iText 是一个工具箱,它允许您创建 PDF,当我仔细查看时,它会让我非常高兴,但如果您不明智地使用该工具箱,它也可以让您创建可怕的 PDF。后者就是您的情况。
你应该解决这个问题,重新使用 PdfStamper
实例,而不是一遍又一遍地创建一个新的 PdfStamper
。如果那不可能,请发布另一个问题,使用更少的文字,准确解释您想要实现的目标。
假设您有许多不同的源文件,其中包含需要添加到单个页面的 PDF 片段。例如:假设每个 PDF 片段都是一张优惠券,您需要创建一个包含 30 张优惠券的工作表。比起使用单个 PdfWriter
实例,使用 getImportedPage()
导入页面并使用 addTemplate()
将它们添加到正确的位置。
当然:我不知道你的项目是关于什么的。页面优惠券的想法受到您的测试 PDF 的启发。
关于java - "Random"生成文档打印失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23657748/