【问题标题】:PDFBox: do PDDocument and PDPage have references to one another?PDFBox:PDDocument 和 PDPage 是否相互引用?
【发布时间】:2019-01-10 00:30:58
【问题描述】:

PDPage 对象是否包含对其所属的 PDDocument 的引用?
换句话说,PDPage 是否知道它的 PDDocument?
在应用程序的某处,我有一个 PDDocument 列表。
这些文档被合并到一个新的 PDDocument 中:

PDFMergerUtility pdfMerger = new PDFMergerUtility();

PDDocument mergedPDDocument = new PDDocument();
for (PDDocument pdfDocument : documentList) {
    pdfMerger.appendDocument(mergedPDDocument, pdfDocument);
}

然后这个 PdDocument 被分成 10 个包:

Splitter splitter = new Splitter();
splitter.setSplitAtPage(bundleSize);
List<PDDocument> bundleList = splitter.split(mergedDocument);

我现在的问题是:
如果我遍历列表中这些拆分的 PDDocument 的页面,有没有办法知道页面最初属于哪个 PDDocument?

另外,如果您有一个 PDPage 对象,您能否从中获取信息,例如页码……? 或者您可以通过其他方式获得它吗?

【问题讨论】:

  • 您问题的第二部分:doc.getPages().indexOf(page)。关于第一部分 - 你的问题的背景是什么?我认为它是“不”,但我刚刚醒来。

标签: java apache pdfbox


【解决方案1】:
  1. PDPage 对象是否包含对其所属PDDocument 的引用?换句话说,PDPage 是否知道其PDDocument

不幸的是,PDPage 不包含对其父 PDDocument 的引用,但它具有文档中所有其他页面的列表,可用于在页面之间导航而无需引用父 PDDocument

  1. 如果您有一个PDPage 对象,您可以从中获取信息,例如其页码,还是可以通过其他方式获取?

有一种解决方法可以在没有PDDocument 可用的情况下获取有关PDPage 在文档中的位置的信息。每个PDPage 都有一个字典,其中包含有关页面大小、资源、字体、内容等的信息。其中一个属性称为Parent,这是一个数组页面,其中包含使用构造函数PDPage(COSDictionary) 创建PDPage 的浅层克隆所需的所有信息。页面顺序正确,因此可以通过数组中记录的位置获取页码。

  1. 如果我遍历列表中这些拆分的PDDocuments 的页面,有没有办法知道一个页面最初属于哪个PDDocument

一旦您将文档列表合并到一个文档中,所有对原始文档的引用都将丢失。您可以通过查看 PDPage 中的 Parent 对象来确认这一点,转到 Parent > Kids > COSObject[n] > Parent 并查看 Parent 的编号是否为数组中的所有元素都相同。在此示例中,所有页面的父级都是 COSName {Parent} : 1781256139;

COSName {Parent} : COSObject {
  COSDictionary {
    COSName {Kids} : COSArray {
      COSObject {
        COSDictionary {
          COSName {TrimBox} : COSArray {0; 0; 612; 792;};
          COSName {MediaBox} : COSArray {0; 0; 612; 792;};
          COSName {CropBox} : COSArray {0; 0; 612; 792;};
          COSName {Resources} : COSDictionary {
            ...
          };
          COSName {Contents} : COSObject {
            ...
          };
          COSName {Parent} : 1781256139;
          COSName {StructParents} : COSInt {68};
          COSName {ArtBox} : COSArray {0; 0; 612; 792; };
          COSName {BleedBox} : COSArray {0; 0; 612; 792; };
          COSName {Type} : COSName {Page};
        }
    }

    ...

    COSName {Count} : COSInt {4};
    COSName {Type} : COSName {Pages};
  }
};

源代码

我编写了以下代码来展示如何使用来自PDPage 字典的信息来前后导航页面并使用数组中的位置获取页码。

public class PDPageUtils {
    public static void main(String[] args) throws InvalidPasswordException, IOException {
        System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");

        PDDocument document = null;
        try {
            String filename = "src/main/resources/pdf/us-017.pdf";
            document = PDDocument.load(new File(filename));

            System.out.println("listIterator(PDPage)");
            ListIterator<PDPage> pageIterator = listIterator(document.getPage(0));
            while (pageIterator.hasNext()) {
                PDPage page = pageIterator.next();
                System.out.println("page #: " + pageIterator.nextIndex() + ", Structural Parent Key: " + page.getStructParents());
            }
        } finally {
            if (document != null) {
                document.close();
            }
        }
    }

    /**
     * Returns a <code>ListIterator</code> initialized with the list of pages from
     * the dictionary embedded in the specified <code>PDPage</code>. The current
     * position of this <code>ListIterator</code> is set to the position of the
     * specified <code>PDPage</code>.
     * 
     * @param page the specified <code>PDPage</code>
     * 
     * @see {@link java.util.ListIterator}
     * @see {@link org.apache.pdfbox.pdmodel.PDPage}
     */
    public static ListIterator<PDPage> listIterator(PDPage page) {
        List<PDPage> pages = new LinkedList<PDPage>();

        COSDictionary pageDictionary = page.getCOSObject();
        COSDictionary parentDictionary = pageDictionary.getCOSDictionary(COSName.PARENT);
        COSArray kidsArray = parentDictionary.getCOSArray(COSName.KIDS);

        List<? extends COSBase> kidList = kidsArray.toList();
        for (COSBase kid : kidList) {
            if (kid instanceof COSObject) {
                COSObject kidObject = (COSObject) kid;
                COSBase type = kidObject.getDictionaryObject(COSName.TYPE);
                if (type == COSName.PAGE) {
                    COSBase kidPageBase = kidObject.getObject();
                    if (kidPageBase instanceof COSDictionary) {
                        COSDictionary kidPageDictionary = (COSDictionary) kidPageBase;
                        pages.add(new PDPage(kidPageDictionary));
                    }
                }
            }
        }
        int index = pages.indexOf(page);
        return pages.listIterator(index);
    }
}

样本输出

在本例中,PDF 文档有 4 页,迭代器用第一页初始化。注意页码是previousIndex()

System.out.println("listIterator(PDPage)");
ListIterator<PDPage> pageIterator = listIterator(document.getPage(0));
while (pageIterator.hasNext()) {
    PDPage page = pageIterator.next();
    System.out.println("page #: " + pageIterator.previousIndex() + ", Structural Parent Key: " + page.getStructParents());
}
列表迭代器(PDPage) 页码:0,结构父键:68 页码:1,结构父键:69 页码:2,结构父键:70 页码:3,结构父键:71

您还可以从最后一页开始向后导航。现在请注意,页码是nextIndex()

ListIterator<PDPage> pageIterator = listIterator(document.getPage(3));
pageIterator.next();
while (pageIterator.hasPrevious()) {
    PDPage page = pageIterator.previous();
    System.out.println("page #: " + pageIterator.nextIndex() + ", Structural Parent Key: " + page.getStructParents());
}
列表迭代器(PDPage) 页码:3,结构父键:71 页码:2,结构父键:70 页码:1,结构父键:69 页码:0,结构父键:68

【讨论】:

  • 如果您使用的是最新的 jdk8,则不再需要 System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");。使用 jdk8 时,警告不再出现在 2.0.13 中。
猜你喜欢
  • 2017-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多