Это сложнее, чем кажется - возможно, вам придется переосмыслить свой вопрос. Гиперссылки внутри документа обычно создаются с помощью аннотации ссылки с назначением, установленным для действия «Перейти к просмотру». Это представление не обязательно включает границы или даже точку. Иногда это просто страница (при текущем масштабе), или страница (по ширине), или страница (вверху, определенный масштаб). И это еще сложнее, потому что место назначения ссылки может быть деревом действий, которые нужно выполнить по порядку, причем каждое действие является одним из 18 различных возможных типов действий, включая javascript, который можно использовать для побуждения зрителя перейти к определенному месту назначения.
Я думаю, у вас также будут проблемы с «в том месте, куда вас ведет ссылка».
Вы можете выполнить большую часть этой задачи на C #, используя Atalasoft dotAnnotate и надстройку извлечения текста PDF (отказ от ответственности, я работаю в Atalasoft, написал импортер PDF-> аннотаций и раньше работал в Adobe над Acrobat v 1, 2 и 3). И нет, извините, это не бесплатное программное обеспечение.
Вот как я бы это сделал (отказ от ответственности - это сразу пришло мне в голову):
class PageAnnots : KeyValuePair<int, List<PdfLinkData>> { }
public PageAnnots GetPageLinkDestinations(Stream stm)
{
PdfAnnotationDataImporter importer = new PdfAnnotationDataImporter(stm);
List<PageAnnots> pageAnnots = new List<PageAnnots>();
try {
importer.Load();
// this gets all annotations on all pages. On long docs, this will be time consuming
AnnotationDataCollection allAnnots = importer.Import();
int pageNo = 0;
// allAnnots is a collection of LayerData, each LayerData object being a collection
// of annots for a page. The collection is empty if there are no annots
foreach (AnnotationData pageOfAnnots in allAnnots) {
List<PdfLinkData> linkAnnots = new List<PdfLinkData>();
LayerData pageLayer = pageOfAnnots as LayerData;
if (pageLayer != null) {
// filter out each annot that is a link
foreach (AnnotationData annot in pageLayer.Items) {
PdfLinkData link = annot as PdfLinkData;
if (link != null)
linkAnnots.Add(link);
}
}
if (linkAnnots.Count > 0) {
pageAnnots.Add(new PageAnnots(pageNo, linkAnnots));
}
pageNo++;
}
}
catch (Exception err) {
// keep it? drop it?
}
return pageAnnots;
}
На данный момент мы сократили его до набора пар ключ-значение, где каждый ключ представляет собой номер страницы, а каждое значение представляет собой непустой список объектов PdfLinkData, представляющих ссылки на этой странице.
Оттуда вы можете перебрать эту коллекцию и попытаться определить место назначения следующим образом:
private int PageFromDestination(PdfDestination dest)
{
PdfIndexedPageReference pageRef = dest.Page as PdfIndexedPageReference;
return pageRef == null ? -1 : pageRef.PageIndex;
}
public void FigureDestination(PdfLinkData link)
{
PdfActionList actions = link.ClickAction;
foreach (PdfAction action in actions) {
PdfGoToViewAction gotoView = action as PdfGoToViewAction;
if (action == null)
continue;
// this only pulls the page from the destination. The dest
// may also contain information about the view. I'm assuming you
// only want the page number
int page = PageFromDestination(gotoView.Destination);
if (page >= 0) {
// here's where you step in - the click action could be
// a long chain of things including several GoToView actions.
// it's up to you to decide what you want to do. Handle only
// action lists of length 1? Stop at first GoToView?
// aggregate them all?
}
}
}
И когда вы посмотрите на этот код, вы удивитесь, зачем существует такой уровень абстракции с точки зрения ссылок на проиндексированные страницы, типов действий и списков действий? Ответ заключается в том, что действие GoToView также может ссылаться на другой документ — перекрестные ссылки на документы допустимы в PDF. Хотя dotAnnotate не поддерживает их прямо сейчас, он готов поддерживать их в будущем. Точно так же действие может означать переход к представлению во встроенном документе PDF (да, вы можете встраивать PDF в PDF).
Вы должны знать, что dotAnnotate предоставляет вам ограниченный набор объектов довольно высокого уровня и не требует от вас знания и понимания спецификации PDF (слишком много). В прошлом мы пытались выпускать очень детализированные API для таких вещей, как TIFF, и обнаружили, что наши клиенты не находят их приемлемыми. Поэтому мы попытались угадать, что, вероятно, захотят и в чем будут нуждаться наши клиенты, и создали API, которые легче усваиваются.
iText и iTextSharp обеспечивают очень точное управление API, но вам необходимо понять спецификацию PDF, чтобы получить то, что вам нужно.
Например, чтобы выполнить извлечение аннотации, вам нужно будет открыть документ, получить каталог страниц, пройтись по дереву страниц, найти все словари страниц, которые имеют ключ аннотаций, пройтись по массиву аннотаций, найти ключ в каждом словаре. /Type со значением /Annot и для ключа /SubType со значением /Link, затем извлеките значение ключа /Dest, если оно присутствует, и если оно не равно нулю, используйте его, в противном случае посмотрите на ключ /A и начните обход дерево действий, чтобы найти действие с ключом /Type, установленным на /GoTo (IIRC), а затем перейти оттуда.
Пункт назначения может быть прямым пунктом назначения или именованным пунктом назначения. Если это именованное место назначения, вам придется вернуться к каталогу документов, вытащить дерево имен и найти в нем имя в именованном месте назначения, а когда вы его найдете, извлечь оттуда информацию.
Так что да, вы можете использовать iText или другой аналогичный анализатор PDF, но вам нужно будет выполнить все эти шаги, если только один из создателей библиотеки не сделает это за вас.
person
plinth
schedule
26.10.2010