これは思ったより難しく、質問を再考する必要があるかもしれません。ドキュメント内のハイパーリンクは通常、リンク先が「Goto View」アクションに設定されたリンク注釈によって行われます。そのビューには、必ずしも境界やポイントが含まれているとは限りません。ページ (現在のズーム) またはページ (幅に合わせる) またはページ (上部、特定のズーム) の場合もあります。さらに複雑なのは、リンク先が順番に実行されるアクションのツリーであり、各アクションが 18 の異なる可能なアクション タイプの 1 つである場合があるためです。これには、視聴者を特定のリンク先に移動させるために使用できる JavaScript が含まれます。
「リンクの先」にも苦労すると思います。
Atalasoft dotAnnotate と PDF テキスト抽出アドオンを使用して、C# でこのタスクの多くを実行できます (免責事項、私は Atalasoft で働いており、PDF-> 注釈インポーターを作成し、Acrobat v 1、2、& で Adobe で働いていました)。 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 仕様を理解する必要があります。
たとえば、注釈の抽出を行うには、ドキュメントを開き、ページ カタログを取得し、ページ ツリーを調べて、Annots キーを持つすべてのページ辞書を見つけ、Annots 配列を調べて、そこにある各辞書でキーを検索する必要があります。値 /Annot を持つ /Type および値 /Link を持つキー /SubType の場合、存在する場合はキー /Dest の値を引き出し、それが null でない場合はそれを使用し、それ以外の場合はキー /A を見て、アクション ツリーを使用して、キー /Type が /GoTo (IIRC) に設定されたアクションを見つけ、そこから移動します。
宛先は、直接の宛先の場合もあれば、名前付きの宛先の場合もあります。名前付き宛先の場合は、文書カタログに戻って名前ツリーを取り出し、名前付き宛先で名前を検索し、見つかったらそこにある情報を引き出す必要があります。
そうです、iText や別の同様の PDF パーサーを使用できますが、ライブラリの作成者の 1 人が親切に対応してくれなければ、これらの手順をすべて実行する必要があります。