【问题标题】:How do I retrieve a page number or page reference for an Outline destination in a PDF on iOS?如何在 iOS 上的 PDF 中检索大纲目标的页码或页面参考?
【发布时间】:2011-06-06 07:43:22
【问题描述】:

我一直在阅读 adobe pdf 规范,以及用于 pdf 渲染和解析的 apple 的石英 2d 文档。我还下载了 Voyeur 并用它检查了本地 pdf 以查看它的内部数据。此时我可以获取文档目录,然后从那里获取大纲字典。我可以看到嵌套在大纲字典字典中,其中有名为“/Dest”的节点,其值如下:

G1.1025588 等等

我想知道是否有一种方法可以让我使用这些值来获取对页面的引用,以使用我见过的一些 github 项目(例如 Reader)以及 Apple 记录的示例来呈现。

PDF 处理绝对是一个挑战,因此我们将不胜感激。

【问题讨论】:

    标签: iphone objective-c pdf ios


    【解决方案1】:

    大纲项字典中的/Dest 条目可以是名称、字符串或数组。

    • 最简单的情况是如果它是一个数组;那么第一项是大纲条目指向的页面对象(字典)。要获取页码,您必须遍历文档中的所有页面并查看哪一页等于 (==) 与您拥有的字典(CGPDFPageRefs 实际上是 CGPDFDictionaryRefs)。您还可以遍历页面树,这有点困难,但可能更快(没有您预期的那么多,我不会在这里过早优化)。数组中的其他项目是页面上的位置等,在 PDF 规范中搜索“Explicit Destinations”以了解更多信息。

    • 如果条目是 name 或 string,则它是命名目的地。您必须将名称映射到文档目录的 /Dests 条目中的目标,该条目是包含名称树的字典。名称树本质上是一个树形图,它允许快速访问命名值,而无需一次读取所有数据(与普通字典一样)。不幸的是,Quartz 中没有对名称树的直接支持,因此您必须做更多的工作来递归解析这个结构(请参阅PDF spec 中的“名称树”)。

    请注意,大纲项不一定有/Dest 条目,它也可以通过/A(动作)条目指定其目的地,这有点复杂。然而,在大多数情况下,该操作将是一个“GoTo”操作,它本质上是一个目的地的包装器。

    名称到目的地的映射也可以存储为普通字典。在这种情况下,它位于文档目录中 /Names 字典的 /Dests 条目中。不过我很少看到这个,它在 PDF 1.2 之后被弃用(当前是 1.7)。

    您肯定需要 PDF 规范:http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf

    【讨论】:

    • 感谢您提供的信息。我现在使用的文档属于 /A 和 Goto 类型。我目前可以在 TOC 中创建一个页面标题。我在大纲中看到了 /A/D/0 页面词典,并在页面中看到了它们对应的 /A/D/0 页面词典。我正在尝试创建标题->页面 nvp 的字典,但我无法将 CGPDFDictionaryRef 添加到 NSDictionary...
    • 我最终通过比较目录页面字典的内容流的字符串值和页面中的实际 pageref 字典来检查是否相等......看起来并不干净但有效。有没有更好的办法?
    • 如何获取内容流的字符串值?你能给我更多的提示吗?就我而言,“/Outlines/.../A/D/0”工作正常,但我无法从“/Annots/.../A/D/0”检查是否相等
    • @sw:当您有两个具有相同内容的页面时,这是行不通的(很少见,但可能)。进行所有字符串比较也很慢。为什么不直接使用将标题和 CGPDFDictionary 存储为实例变量的自定义模型类?
    【解决方案2】:

    感谢 Omz,这里有一段代码可以检索 PDF 文件中大纲目标的页码:

    // Get Page Number from an array
    - (int) getPageNumberFromArray:(CGPDFArrayRef)array ofPdfDoc:(CGPDFDocumentRef)pdfDoc withNumberOfPages:(int)numberOfPages
    {
        int pageNumber = -1;
    
        // Page number reference is the first element of array (el 0)
        CGPDFDictionaryRef pageDic;
        CGPDFArrayGetDictionary(array, 0, &pageDic);
    
        // page searching
        for (int p=1; p<=numberOfPages; p++)
        {
            CGPDFPageRef page = CGPDFDocumentGetPage(pdfDoc, p);
            if (CGPDFPageGetDictionary(page) == pageDic)
            {
                pageNumber = p;
                break;
            }
        }
    
        return pageNumber;
    }
    
    // Get page number from an outline. Only support "Dest" and "A" entries
    - (int) getPageNumber:(CGPDFDictionaryRef)node ofPdfDoc:(CGPDFDocumentRef)pdfDoc withNumberOfPages:(int)numberOfPages
    {
        int pageNumber = -1;
    
        CGPDFArrayRef destArray;
        CGPDFDictionaryRef dicoActions;
        if(CGPDFDictionaryGetArray(node, "Dest", &destArray))
        {
            pageNumber = [self getPageNumberFromArray:destArray ofPdfDoc:pdfDoc withNumberOfPages:numberOfPages];
        }
        else if(CGPDFDictionaryGetDictionary(node, "A", &dicoActions))
        {
            const char * typeOfActionConstChar;
            CGPDFDictionaryGetName(dicoActions, "S", &typeOfActionConstChar);
    
            NSString * typeOfAction = [NSString stringWithUTF8String:typeOfActionConstChar];
            if([typeOfAction isEqualToString:@"GoTo"]) // only support "GoTo" entry. See PDF spec p653
            {
                CGPDFArrayRef dArray;
                if(CGPDFDictionaryGetArray(dicoActions, "D", &dArray)) 
                {
                    pageNumber = [self getPageNumberFromArray:dArray ofPdfDoc:pdfDoc withNumberOfPages:numberOfPages];
                }
            }
        }
    
        return pageNumber;
    }
    

    【讨论】:

      猜你喜欢
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      • 1970-01-01
      • 2010-09-08
      • 2011-03-15
      相关资源
      最近更新 更多