Как получить номер страницы или ссылку на страницу для назначения контура в PDF на iOS?

Я читал спецификацию Adobe adobe pdf вместе с документацией на яблочный кварц 2d для рендеринга и анализа PDF. Я также загрузил Voyeur и проверил локальный файл pdf, чтобы увидеть его внутренние данные. На этом этапе я могу получить каталог документов, а затем получить словарь контуров оттуда. Я вижу, что вложенные в словарные словари очертания обозначают узлы "/Dest" с такими значениями, как:

G1.1025588 и т.д.

Мне интересно, есть ли способ использовать эти значения, чтобы получить ссылку на страницу для рендеринга, используя некоторые методы, которые я видел в проектах github, таких как Reader, а также примеры, иллюстрированные яблоком.

Обработка PDF - определенно проблема, поэтому любая помощь будет оценена.

Ответ 1

В записи /Dest в словаре элементов схемы может быть имя, строка или массив .

  • Самый простой случай - массив ; то первым элементом является объект страницы, на который указывает точка перехода (словарь). Чтобы получить номер страницы, вам нужно выполнить итерацию по всем страницам документа и посмотреть, какой из них равен (==), который у вас есть (CGPDFPageRef на самом деле CGPDFDictionaryRef s). Вы также можете пересечь дерево страниц, что немного сложнее, но может быть быстрее (не так сильно, как вы могли бы ожидать, я бы не оптимизировал преждевременно здесь). Другие элементы в массиве - это позиция на странице и т.д., Найдите "Явные адресаты" в спецификации PDF, чтобы узнать больше.

  • Если запись является именем или строкой, это именованное место назначения. Вы должны сопоставить имя с пунктом назначения из каталога каталога /Dests, который является словарем, который содержит дерево имен. Дерево имен - это, по существу, древовидная карта, которая обеспечивает быстрый доступ к именованным значениям без необходимости считывать сразу все данные (как и в обычном словаре). К сожалению, нет прямой поддержки деревьев имен в Quartz, поэтому вам придется немного поработать, чтобы проанализировать эту структуру рекурсивно (см. "Деревья имен" в Спецификация PDF).

Обратите внимание, что элемент контура необязательно имеет запись /Dest, он также может указать свой пункт назначения через запись /A (действие), которая немного сложнее. Однако в большинстве случаев действие будет действовать как "GoTo", которое по существу является оберткой для адресата.

Отображение имен для адресатов также может быть сохранено в виде простого словаря. В этом случае он находится в записи /Dests в словаре/Имена в каталоге документов. Я редко видел это, хотя и был устаревшим после PDF 1.2 (текущий - 1.7).

Вам обязательно понадобится спецификация PDF для этого: http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf

Ответ 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;
}