使用 PdfWriter 而不是 PdfCopy 复制注释
Copying annotations with PdfWriter instead of PdfCopy
我需要使用 PdfWriter 而不是 PdfCopy 来复制注释,因为在复制时我需要 resize/rotate 页面。谁能告诉我怎么做?
您认为您需要使用普通 PdfWriter
而不是 PdfCopy
来复制 PDF,因为您 需要 resize/rotate 页面 和 iText in Action,第 2 版表示,PdfCopy
class 无法做到这一点。因此,您正在寻找一种在这种情况下复制注释的方法。
您应该寻找的是一种旋转或调整页面大小的方法,同时仍然使用 PdfCopy
!
虽然 PdfCopy
class 本身 确实不允许调整页面大小或旋转页面,但您可以操作加载到 PdfReader
并调整大小 and/or 使用 PdfCopy
class 在 之前旋转其页面 。如果您随后将页面从这个被操纵的 PdfReader
复制到 PdfCopy
,您会得到一个调整大小或旋转页面的结果(由于被操纵的 PdfReader
)和所有存在的注释(由于使用 PdfCopy
).
例如您可以像这样调整 PdfReader
中所有页面的大小:
void resize(PdfReader pdfReader, float width, float height) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
boolean switched = pdfReader.getPageRotation(i) % 180 != 0;
float widthHere = switched ? height : width;
float heightHere = switched ? width : height;
Rectangle cropBox = pdfReader.getCropBox(i);
float halfWidthGain = (widthHere - cropBox.getWidth()) / 2;
float halfHeightGain = (heightHere - cropBox.getHeight()) / 2;
Rectangle newCropBox = new Rectangle(cropBox.getLeft() - halfWidthGain, cropBox.getBottom() - halfHeightGain,
cropBox.getRight() + halfWidthGain, cropBox.getTop() + halfHeightGain);
Rectangle mediaBox = pdfReader.getPageSize(i);
Rectangle newMediaBox = new Rectangle(Math.min(newCropBox.getLeft(), mediaBox.getLeft()),
Math.min(newCropBox.getBottom(), mediaBox.getBottom()),
Math.max(newCropBox.getRight(), mediaBox.getRight()),
Math.max(newCropBox.getTop(), mediaBox.getTop()));
PdfDictionary pageDictionary = pdfReader.getPageN(i);
pageDictionary.put(PdfName.MEDIABOX, new PdfArray(new float[] {newMediaBox.getLeft(), newMediaBox.getBottom(),
newMediaBox.getRight(), newMediaBox.getTop()}));
pageDictionary.put(PdfName.CROPBOX, new PdfArray(new float[] {newCropBox.getLeft(), newCropBox.getBottom(),
newCropBox.getRight(), newCropBox.getTop()}));
}
}
(CopyWithResizeRotate辅助方法)
您可以像这样旋转 PdfReader
中的所有页面:
void rotate(PdfReader pdfReader) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
int rotation = pdfReader.getPageRotation(i);
int newRotation = rotation + 90 % 360;
PdfDictionary pageDictionary = pdfReader.getPageN(i);
if (newRotation == 0)
pageDictionary.remove(PdfName.ROTATE);
else
pageDictionary.put(PdfName.ROTATE, new PdfNumber(newRotation));
}
}
(CopyWithResizeRotate辅助方法)
使用这些助手,您可以例如从一些源 PDF 的旋转 and/or 调整大小的页面创建一个 PDF 并像这样复制它们:
byte[] wildPdf = RETRIEVE_SOURCE_PDF;
PdfReader pdfReaderOriginal = new PdfReader(wildPdf);
PdfReader pdfReaderRotate = new PdfReader(wildPdf);
rotate(pdfReaderRotate);
PdfReader pdfReaderResize = new PdfReader(wildPdf);
resize(pdfReaderResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
PdfReader pdfReaderRotateResize = new PdfReader(wildPdf);
rotate(pdfReaderRotateResize);
resize(pdfReaderRotateResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
try ( OutputStream os = new FileOutputStream(new File(RESULT_FOLDER, "wild-rotated-resized.pdf"))) {
Document document = new Document();
PdfCopy pdfCopy = new PdfCopy(document, os);
document.open();
pdfCopy.addDocument(pdfReaderOriginal);
pdfCopy.addDocument(pdfReaderRotate);
pdfCopy.addDocument(pdfReaderResize);
pdfCopy.addDocument(pdfReaderRotateResize);
document.close();
}
(CopyWithResizeRotate测试方法testRotateResizeAndCopy
)
结果如下所示,第一行是原始页面(#1 A4、#2 HALFLETTER、#3 A5、#4 A5 已旋转、#5 500x700),第二行是旋转后的页面,第三行行调整大小的(到 LETTER),第四行是旋转和调整大小的(到 LETTER)。遗憾的是,Adobe Reader 缩略图根本无法按比例缩放:
仅处理单个 PDF
如果您实际上只想 resize/rotate 单个输入 PDF 的页面,则不应使用 PdfCopy
实例,而应使用 PdfStamper
:
PdfReader pdfReader = new PdfReader(SOURCE);
[...manipulate properties of the pdfReader like above...]
new PdfStamper(pdfReader, TARGET_STREAM).close();
这里的好处是不仅保留了原始文档的页面级数据,还保留了文档级数据。
特殊注释
有一种类型的注释会在上面的代码中以意想不到的方式运行:设置了 NoRotate 标志的注释。当它们的主页旋转时,此类注释将表现如下:
(ISO 32000-2 第 12.5.3 节 — 注释标志)
我需要使用 PdfWriter 而不是 PdfCopy 来复制注释,因为在复制时我需要 resize/rotate 页面。谁能告诉我怎么做?
您认为您需要使用普通 PdfWriter
而不是 PdfCopy
来复制 PDF,因为您 需要 resize/rotate 页面 和 iText in Action,第 2 版表示,PdfCopy
class 无法做到这一点。因此,您正在寻找一种在这种情况下复制注释的方法。
您应该寻找的是一种旋转或调整页面大小的方法,同时仍然使用 PdfCopy
!
虽然 PdfCopy
class 本身 确实不允许调整页面大小或旋转页面,但您可以操作加载到 PdfReader
并调整大小 and/or 使用 PdfCopy
class 在 之前旋转其页面 。如果您随后将页面从这个被操纵的 PdfReader
复制到 PdfCopy
,您会得到一个调整大小或旋转页面的结果(由于被操纵的 PdfReader
)和所有存在的注释(由于使用 PdfCopy
).
例如您可以像这样调整 PdfReader
中所有页面的大小:
void resize(PdfReader pdfReader, float width, float height) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
boolean switched = pdfReader.getPageRotation(i) % 180 != 0;
float widthHere = switched ? height : width;
float heightHere = switched ? width : height;
Rectangle cropBox = pdfReader.getCropBox(i);
float halfWidthGain = (widthHere - cropBox.getWidth()) / 2;
float halfHeightGain = (heightHere - cropBox.getHeight()) / 2;
Rectangle newCropBox = new Rectangle(cropBox.getLeft() - halfWidthGain, cropBox.getBottom() - halfHeightGain,
cropBox.getRight() + halfWidthGain, cropBox.getTop() + halfHeightGain);
Rectangle mediaBox = pdfReader.getPageSize(i);
Rectangle newMediaBox = new Rectangle(Math.min(newCropBox.getLeft(), mediaBox.getLeft()),
Math.min(newCropBox.getBottom(), mediaBox.getBottom()),
Math.max(newCropBox.getRight(), mediaBox.getRight()),
Math.max(newCropBox.getTop(), mediaBox.getTop()));
PdfDictionary pageDictionary = pdfReader.getPageN(i);
pageDictionary.put(PdfName.MEDIABOX, new PdfArray(new float[] {newMediaBox.getLeft(), newMediaBox.getBottom(),
newMediaBox.getRight(), newMediaBox.getTop()}));
pageDictionary.put(PdfName.CROPBOX, new PdfArray(new float[] {newCropBox.getLeft(), newCropBox.getBottom(),
newCropBox.getRight(), newCropBox.getTop()}));
}
}
(CopyWithResizeRotate辅助方法)
您可以像这样旋转 PdfReader
中的所有页面:
void rotate(PdfReader pdfReader) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
int rotation = pdfReader.getPageRotation(i);
int newRotation = rotation + 90 % 360;
PdfDictionary pageDictionary = pdfReader.getPageN(i);
if (newRotation == 0)
pageDictionary.remove(PdfName.ROTATE);
else
pageDictionary.put(PdfName.ROTATE, new PdfNumber(newRotation));
}
}
(CopyWithResizeRotate辅助方法)
使用这些助手,您可以例如从一些源 PDF 的旋转 and/or 调整大小的页面创建一个 PDF 并像这样复制它们:
byte[] wildPdf = RETRIEVE_SOURCE_PDF;
PdfReader pdfReaderOriginal = new PdfReader(wildPdf);
PdfReader pdfReaderRotate = new PdfReader(wildPdf);
rotate(pdfReaderRotate);
PdfReader pdfReaderResize = new PdfReader(wildPdf);
resize(pdfReaderResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
PdfReader pdfReaderRotateResize = new PdfReader(wildPdf);
rotate(pdfReaderRotateResize);
resize(pdfReaderRotateResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
try ( OutputStream os = new FileOutputStream(new File(RESULT_FOLDER, "wild-rotated-resized.pdf"))) {
Document document = new Document();
PdfCopy pdfCopy = new PdfCopy(document, os);
document.open();
pdfCopy.addDocument(pdfReaderOriginal);
pdfCopy.addDocument(pdfReaderRotate);
pdfCopy.addDocument(pdfReaderResize);
pdfCopy.addDocument(pdfReaderRotateResize);
document.close();
}
(CopyWithResizeRotate测试方法testRotateResizeAndCopy
)
结果如下所示,第一行是原始页面(#1 A4、#2 HALFLETTER、#3 A5、#4 A5 已旋转、#5 500x700),第二行是旋转后的页面,第三行行调整大小的(到 LETTER),第四行是旋转和调整大小的(到 LETTER)。遗憾的是,Adobe Reader 缩略图根本无法按比例缩放:
仅处理单个 PDF
如果您实际上只想 resize/rotate 单个输入 PDF 的页面,则不应使用 PdfCopy
实例,而应使用 PdfStamper
:
PdfReader pdfReader = new PdfReader(SOURCE);
[...manipulate properties of the pdfReader like above...]
new PdfStamper(pdfReader, TARGET_STREAM).close();
这里的好处是不仅保留了原始文档的页面级数据,还保留了文档级数据。
特殊注释
有一种类型的注释会在上面的代码中以意想不到的方式运行:设置了 NoRotate 标志的注释。当它们的主页旋转时,此类注释将表现如下:
(ISO 32000-2 第 12.5.3 节 — 注释标志)