我正在维护一个使用iText 2.1.7创建PDF的web应用程序。我想拿一个现有PDF的内容,并把它放在pdf文档,代码是在创建过程中。我有以下内容(编辑:更完整的代码):
package itexttest;
import com.lowagie.text.Document;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
public class ITextTest
{
public static void main(String[] args)
{
try
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
Document bigDoc = new Document(PageSize.LETTER, 50, 50, 110, 60);
PdfWriter writer = PdfWriter.getInstance(bigDoc, os);
bigDoc.open();
Paragraph par = new Paragraph("one");
bigDoc.add(par);
bigDoc.add(new Paragraph("three"));
addPdfPage(bigDoc, os, "c:/insertable.pdf");
bigDoc.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static void addPdfPage(Document document, OutputStream outputStream, String location) {
try {
PdfReader pdfReader = new PdfReader(location);
int pages = pdfReader.getNumberOfPages();
PdfCopy pdfCopy = new PdfCopy(document, outputStream);
PdfImportedPage page = pdfCopy.getImportedPage(pdfReader, 1);
pdfCopy.addPage(page);
}
catch (Exception e) {
System.out.println("Cannot add PDF from PSC: <" + location + ">: " + e.getMessage());
e.printStackTrace();
}
}
}
这会引发一个错误,PdfWriter.getPageReference()
为null。
我怎么会不正确地使用这个?如何从现有文档中获取页面并将其放入当前文档中?请注意,我所处的地方根本不方便将文件写入临时存储或其他任何东西。
发布于 2016-01-18 14:31:36
我不再积极地使用旧的iText版本了,但是从那以后有些事情没有改变。因此,在这里,代码和指针中的一些问题有助于解决这些问题:
当前代码中的主要问题是
Document
实例(您已经为PdfWriter
使用并已经打开)用于PdfCopy
;虽然Document
可以支持多个侦听器,但它们都需要在调用open
之前注册;此构造的用例是以两种不同格式并行创建同一个文档;PdfWriter
和PdfCopy
都使用相同的输出流;结果不是一个有效的PDF,而是字节范围从两个不同的PDF广泛混合在一起,即肯定不会是一个有效的PDF。正确使用PdfCopy
您可以通过首先在ByteArrayOutputStream
中创建包含新段落的新PDF (关闭所涉及的Document
)来重构代码,然后将此PDF和要添加到新PDF中的其他页面复制。
例如:
ByteArrayOutputStream os = new ByteArrayOutputStream();
Document bigDoc = new Document(PageSize.LETTER, 50, 50, 110, 60);
PdfWriter writer = PdfWriter.getInstance(bigDoc, os);
bigDoc.open();
Paragraph par = new Paragraph("one");
bigDoc.add(par);
bigDoc.add(new Paragraph("three"));
bigDoc.close();
ByteArrayOutputStream os2 = new ByteArrayOutputStream();
Document finalDoc = new Document();
PdfCopy copy = new PdfCopy(finalDoc, new FileOutputStream(RESULT2));
finalDoc.open();
PdfReader reader = new PdfReader(os.toByteArray());
for (int i = 0; i < reader.getNumberOfPages();) {
copy.addPage(copy.getImportedPage(reader, ++i));
}
PdfReader pdfReader = new PdfReader("c:/insertable.pdf");
copy.addPage(copy.getImportedPage(pdfReader, 1));
finalDoc.close();
reader.close();
pdfReader.close();
// result PDF
byte[] result = os2.toByteArray();
只使用PdfWriter
您也可以通过直接将页面导入PdfWriter
来更改代码,例如:
ByteArrayOutputStream os = new ByteArrayOutputStream();
Document bigDoc = new Document(PageSize.LETTER, 50, 50, 110, 60);
PdfWriter writer = PdfWriter.getInstance(bigDoc, os);
bigDoc.open();
Paragraph par = new Paragraph("one");
bigDoc.add(par);
bigDoc.add(new Paragraph("three"));
PdfReader pdfReader = new PdfReader("c:/insertable.pdf");
PdfImportedPage page = writer.getImportedPage(pdfReader, 1);
bigDoc.newPage();
PdfContentByte canvas = writer.getDirectContent();
canvas.addTemplate(page, 1, 0, 0, 1, 0, 0);
bigDoc.close();
pdfReader.close();
// result PDF
byte[] result = os.toByteArray();
这种方法看起来更好,因为不需要中间PDF。不幸的是,这种外表是骗人的,这种做法是有一些缺点的。
这里,并不是像文档一样复制和添加整个原始页面,而是只将其内容流用作模板的内容,然后从实际的新文档页中引用该模板。这尤其意味着:
(顺便说一句,这些模板在通用的PDF规范术语中称为表单XObjects。)
This answer在合并PDF的上下文中显式地处理PdfCopy
和PdfWriter
的使用。
发布于 2016-01-18 17:48:42
下面是另一个版本,包含了mkl的更正,希望这些名字能够回答其他问题:
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import com.lowagie.text.Document;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
public class PdfPlay
{
public static void main(String[] args)
{
try
{
ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream();
Document document1 = new Document(PageSize.LETTER, 50, 50, 110, 60);
PdfWriter writer1 = PdfWriter.getInstance(document1, outputStream1);
document1.open();
document1.add(new Paragraph("one"));
document1.add(new Paragraph("two"));
document1.add(new Paragraph("three"));
document1.close();
byte[] withInsert = addPdfPage(outputStream1, "insertable.pdf");
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static byte[] addPdfPage(ByteArrayOutputStream outputStream1, String insertFilename)
{
try
{
ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream();
Document document2 = new Document();
PdfCopy copy = new PdfCopy(document2, new FileOutputStream("inserted.pdf"));
document2.open();
PdfReader outputStream1Reader = new PdfReader(outputStream1.toByteArray());
for (int i=1; i<=outputStream1Reader.getNumberOfPages(); i++)
{
copy.addPage(copy.getImportedPage(outputStream1Reader, i));
}
PdfReader insertReader = new PdfReader(insertFilename);
copy.addPage(copy.getImportedPage(insertReader, 1));
document2.close();
outputStream1Reader.close();
insertReader.close();
byte[] result = outputStream2.toByteArray();
return result;
}
catch (Exception e)
{
System.out.println("Cannot add PDF from PSC: <" + insertFilename + ">: " + e.getMessage());
e.printStackTrace();
return null;
}
}
}
如果在程序的默认目录中与文件'insertable.pdf‘一起运行,该程序将在同一个目录中生成文件'inserted.pdf’,其中第一页上有文本“一”、“二”和“三”,第二页上有'insertable.pdf‘的第一页。
因此,mkl的修正是有效的;要在我想使用它的环境中使用它,有几个问题:
我有一个程序,我想要使用这个功能,这是一个web应用程序,所以没有现成的访问一个地方写一个文件。我假设我可以使用ByteArrayOutputStream代替输出文件,就像这里所做的那样。
为了插入内容,绝对有必要创建一个新的输出流吗?我希望找到一种方法来告诉某些iText组件:“这是一个文件;阅读它的第一页并将其插入我已经打开的文档/outputStream/writer中。在内存中的Pdf文档可能会变得相当大;我宁愿不必复制所有现有的PDF结构,这样我就可以添加另一个结构。如果我最终从多个文档中插入页面,我想我可能需要多次这样做,我想……
https://stackoverflow.com/questions/34818288
复制相似问题