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

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

G1.1025588 и т. Д.

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

Обработка PDF определенно является проблемой, поэтому любая помощь будет принята с благодарностью.

2 ответа

Решение

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

  • Самый простой случай, если это массив; затем первый элемент - это объект страницы, на который указывает элемент схемы (словарь). Чтобы получить номер страницы, вам нужно перебрать все страницы документа и посмотреть, какая из них равна (==) к словарю у вас есть (CGPDFPageRefс на самом деле CGPDFDictionaryRefс). Вы также можете пройти по дереву страниц, которое немного сложнее, но может быть быстрее (не так много, как вы ожидаете, я бы здесь не оптимизировал преждевременно). Другие элементы в массиве - это положение на странице и т. Д., Для получения дополнительной информации ищите "Явные места назначения" в спецификации 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

Благодаря 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;
}
Другие вопросы по тегам