更改 PDF 文件中轮廓(书签)的缩放级别
Change zoom level in outlines (bookmarks) in PDF files
我想使用 iText7
更改轮廓中的缩放级别。请注意,与 iText 5.
中的方式相比,这可能已经发生了变化
通过反复试验,我想出了代码:
List<PdfOutline> outlines = pdfDoc.getOutlines(true).getAllChildren();
for (int i = 0; i < outlines.size(); i++) {
PdfOutline outline = outlines.get(i);
PdfDictionary content = outline.getContent();
PdfDictionary pdfDictionary = (PdfDictionary) content.get(PdfName.A);
if (pdfDictionary != null) {
PdfArray arr = (PdfArray) pdfDictionary.get(PdfName.D);
if (arr.size() == 5) { // for XYZ zoom type
PdfName xyz = (PdfName) arr.get(1);
arr.set(3, new PdfNumber(2_000));
arr.set(4, new PdfNumber(2_000));
}
}
编辑
问题是上面的代码似乎不起作用,因为生成的 pdf 已保存,但缩放级别没有变化。
更新
我想出了一个不同的解决方案(灵感来自 ):
PdfNameTree destsTree = document.getCatalog().getNameTree(PdfName.Dests);
PdfOutline outline = document.getOutlines(false);
if (outline != null) {
walkOutlines(outline, destsTree.getNames(), document);
}
private static void walkOutlines(PdfOutline outline, Map<String, PdfObject> names,
PdfDocument document) {
if (outline.getDestination() != null) {
int pageNumber = document.getPageNumber(
(PdfDictionary) outline.getDestination().getDestinationPage(names));
float height = document.getPage(pageNumber).getPageSize().getHeight();
outline.setOpen(false);
outline.addDestination(PdfExplicitRemoteGoToDestination.createXYZ(
pageNumber, 0F, height, ZOOM_LEVEL));
}
for (PdfOutline child : outline.getAllChildren()) {
walkOutlines(child, names, document);
}
}
是这样的吗?
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
pdfDoc.getCatalog().setOpenAction(PdfExplicitDestination.createXYZ(pdfDoc.getPage(1), 0, pdfDoc.getPage(1).getPageSize().getHeight(), 0.75f));
pdfDoc.close();
您的原始代码
The problem is that the above code doesn't seem to work as the resulting pdf is saved but there are no changes in zoom level.
尝试你的原始代码我无法确认这一点,标题页轮廓的缩放级别和y坐标(你为什么也改变它?)被改变了!
但其他大纲未更改,原因有二:
您的原始代码仅迭代顶层大纲(直接目录大纲children),因此根本只能更改顶层大纲条目。
您的原始代码假设大纲有一个 GoTo Action (A) 包装一个明确的 XYZ 目标(D 映射到 5 元素数组)。
如果您的文档仅适用于扉页,所有其他大纲立即包含一个 non-explicit,名为 Destination(Dest 映射到名称)。
您的替代代码
您的替代代码确实解决了问题,特别是
- 它还递归地访问顶层大纲的所有祖先,因此所有大纲条目都可以更改;和
- 它使用 iText 类 来识别和评估各种目的地,无论它们是否包含在动作中,无论它们是明确的还是命名的,...
在 Adobe Reader 中,结果似乎符合预期,但仔细研究后发现存在问题:
- 扉页大纲(这是您唯一一个带有 Action 条目的大纲)现在有 both 旧的 Action 条目和一个新的 Destination 条目。严格来说这是规范所禁止的;因此,您应该删除现有的 Action 条目。
- 创建的 destination 数组包含作为整数页码的目标页面(第一个数组条目)。这仅适用于 Remote GoTo Actions 中的目的地。由于您的新目的地显然是 non-Remote 和 non-Actions,这只是因为 Adobe Reader 非常宽松。您应该使用页码 object 而不是页码,并且您不应该使用
PdfExplicitRemoteGoToDestination
作为开头。
改进您的替代代码
要解决上述问题,请更改
outline.addDestination(PdfExplicitRemoteGoToDestination.createXYZ(
pageNumber, 0F, height, ZOOM_LEVEL));
到
outline.getContent().remove(PdfName.A);
outline.addDestination(PdfExplicitDestination.createXYZ(
document.GetPage(pageNumber), 0F, height, ZOOM_LEVEL));
严格来说,您的代码可以进一步改进。
- 您使用
PageSize
但对于可见页面区域,您应该使用 CropBox
。
- 并且你使用
0F, height
作为目标视图左上角的坐标。这假定坐标系的原点是页面的左下角您应该使用 box.getLeft(), box.getTop()
而不是 box
作为相关页面的裁剪框。
不过,对于您的示例文档,媒体框和裁剪框重合,原点确实是左下角。因此,对于该文档,您不需要该改进。
我想使用 iText7
更改轮廓中的缩放级别。请注意,与 iText 5.
通过反复试验,我想出了代码:
List<PdfOutline> outlines = pdfDoc.getOutlines(true).getAllChildren();
for (int i = 0; i < outlines.size(); i++) {
PdfOutline outline = outlines.get(i);
PdfDictionary content = outline.getContent();
PdfDictionary pdfDictionary = (PdfDictionary) content.get(PdfName.A);
if (pdfDictionary != null) {
PdfArray arr = (PdfArray) pdfDictionary.get(PdfName.D);
if (arr.size() == 5) { // for XYZ zoom type
PdfName xyz = (PdfName) arr.get(1);
arr.set(3, new PdfNumber(2_000));
arr.set(4, new PdfNumber(2_000));
}
}
编辑
问题是上面的代码似乎不起作用,因为生成的 pdf 已保存,但缩放级别没有变化。
更新
我想出了一个不同的解决方案(灵感来自
PdfNameTree destsTree = document.getCatalog().getNameTree(PdfName.Dests);
PdfOutline outline = document.getOutlines(false);
if (outline != null) {
walkOutlines(outline, destsTree.getNames(), document);
}
private static void walkOutlines(PdfOutline outline, Map<String, PdfObject> names,
PdfDocument document) {
if (outline.getDestination() != null) {
int pageNumber = document.getPageNumber(
(PdfDictionary) outline.getDestination().getDestinationPage(names));
float height = document.getPage(pageNumber).getPageSize().getHeight();
outline.setOpen(false);
outline.addDestination(PdfExplicitRemoteGoToDestination.createXYZ(
pageNumber, 0F, height, ZOOM_LEVEL));
}
for (PdfOutline child : outline.getAllChildren()) {
walkOutlines(child, names, document);
}
}
是这样的吗?
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
pdfDoc.getCatalog().setOpenAction(PdfExplicitDestination.createXYZ(pdfDoc.getPage(1), 0, pdfDoc.getPage(1).getPageSize().getHeight(), 0.75f));
pdfDoc.close();
您的原始代码
The problem is that the above code doesn't seem to work as the resulting pdf is saved but there are no changes in zoom level.
尝试你的原始代码我无法确认这一点,标题页轮廓的缩放级别和y坐标(你为什么也改变它?)被改变了!
但其他大纲未更改,原因有二:
您的原始代码仅迭代顶层大纲(直接目录大纲children),因此根本只能更改顶层大纲条目。
您的原始代码假设大纲有一个 GoTo Action (A) 包装一个明确的 XYZ 目标(D 映射到 5 元素数组)。
如果您的文档仅适用于扉页,所有其他大纲立即包含一个 non-explicit,名为 Destination(Dest 映射到名称)。
您的替代代码
您的替代代码确实解决了问题,特别是
- 它还递归地访问顶层大纲的所有祖先,因此所有大纲条目都可以更改;和
- 它使用 iText 类 来识别和评估各种目的地,无论它们是否包含在动作中,无论它们是明确的还是命名的,...
在 Adobe Reader 中,结果似乎符合预期,但仔细研究后发现存在问题:
- 扉页大纲(这是您唯一一个带有 Action 条目的大纲)现在有 both 旧的 Action 条目和一个新的 Destination 条目。严格来说这是规范所禁止的;因此,您应该删除现有的 Action 条目。
- 创建的 destination 数组包含作为整数页码的目标页面(第一个数组条目)。这仅适用于 Remote GoTo Actions 中的目的地。由于您的新目的地显然是 non-Remote 和 non-Actions,这只是因为 Adobe Reader 非常宽松。您应该使用页码 object 而不是页码,并且您不应该使用
PdfExplicitRemoteGoToDestination
作为开头。
改进您的替代代码
要解决上述问题,请更改
outline.addDestination(PdfExplicitRemoteGoToDestination.createXYZ(
pageNumber, 0F, height, ZOOM_LEVEL));
到
outline.getContent().remove(PdfName.A);
outline.addDestination(PdfExplicitDestination.createXYZ(
document.GetPage(pageNumber), 0F, height, ZOOM_LEVEL));
严格来说,您的代码可以进一步改进。
- 您使用
PageSize
但对于可见页面区域,您应该使用CropBox
。 - 并且你使用
0F, height
作为目标视图左上角的坐标。这假定坐标系的原点是页面的左下角您应该使用box.getLeft(), box.getTop()
而不是box
作为相关页面的裁剪框。
不过,对于您的示例文档,媒体框和裁剪框重合,原点确实是左下角。因此,对于该文档,您不需要该改进。