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