Как получить номер страницы или ссылку на страницу для места назначения 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;
}