前回の続きでPDFのNamed Destinationからページ番号を得る方法を考えてみました.
例えば次のようなtopicがあったとします.
<topic id="topic_14F0A9302584C94D">
<title>Topic title</title>
...
</topic>
<title>Topic title</title>
...
</topic>
通常DITA Open Toolkitの中間ファイルではtopicが必ずユニークなidを持つことを前提とせずに、独自に"unique_NN"というようなidを再割り当てしてしまいますが、それでもこのidは@oidという属性で残してくれてあります.これを利用して、Named DestinationとしてXSL-FOを生成しPDFに登録してやります.
このFOから作成されたPDFを強引にテキストエディタで覗くと次のようなエントリーを見つけることができます.
1161 0 obj
<<
/Type /Catalog /Pages 1 0 R
/Outlines 1046 0 R/PageMode /UseOutlines
/PageLabels
<< /Nums [ 0 << /S /D >>
1 << /S /r >>
7 << /S /D >> ] >>
/Names 1162 0 R /Lang (EN) >>
endobj
<<
/Type /Catalog /Pages 1 0 R
/Outlines 1046 0 R/PageMode /UseOutlines
/PageLabels
<< /Nums [ 0 << /S /D >>
1 << /S /r >>
7 << /S /D >> ] >>
/Names 1162 0 R /Lang (EN) >>
endobj
これはカタログオブジェクトで、/Namesで示されたオブジェクトがNamed Destinationのエントリです.
1162 0 obj
<< /Dests 1163 0 R >>
endobj
<< /Dests 1163 0 R >>
endobj
1163 0 obj
<< /Kids [ 1164 0 R 1165 0 R 1166 0 R 1167 0 R 1168 0 R 1169 0 R 1170 0 R ]
>>
endobj
<< /Kids [ 1164 0 R 1165 0 R 1166 0 R 1167 0 R 1168 0 R 1169 0 R 1170 0 R ]
>>
endobj
とたどってゆくと次のようなオブジェクトのエントリを見つけることができます.(すごく簡略化しています.)
1167 0 obj
<< /Limits [ (...) (...) ]
/Names [ (...) 699 0 R
...
(topic_14F0A9302584C94D) 12 0 R
...
>>
endobj
<< /Limits [ (...) (...) ]
/Names [ (...) 699 0 R
...
(topic_14F0A9302584C94D) 12 0 R
...
>>
endobj
ここには、さっきのtopicのidが格納されています.この12 0 Rを調べると
12 0 obj
<< /D [ 10 0 R /XYZ null 720 null ]
>>
endobj
<< /D [ 10 0 R /XYZ null 720 null ]
>>
endobj
とNamed Destinationの定義があります.そしてこの 10 0 をたどると、
10 0 obj
<</Type /Page
/Trans << >>
/Parent 2 0 R /Resources <</Font <<
/F2 20 0 R/F3 21 0 R >>
/ProcSet [/PDF/Text/ImageC]
>>
<</Type /Page
/Trans << >>
/Parent 2 0 R /Resources <</Font <<
/F2 20 0 R/F3 21 0 R >>
/ProcSet [/PDF/Text/ImageC]
>>
ということでようやくこのtopicが位置するページオブジェクトにたどり着くことができました.これが目的のページです.
このページオブジェクトは
2 0 obj
<<
/Type /Pages
/Kids [ 3 0 R 10 0 R 23 0 R 40 0 R 50 0 R 56 0 R 61 0 R 66 0 R 70 0 R 86 0 R
...
713 0 R]
/Count 71
/Parent 1 0 R >>
endobj
<<
/Type /Pages
/Kids [ 3 0 R 10 0 R 23 0 R 40 0 R 50 0 R 56 0 R 61 0 R 66 0 R 70 0 R 86 0 R
...
713 0 R]
/Count 71
/Parent 1 0 R >>
endobj
というページ辞書を参照することで物理的に2ページ目であることがわかります.でもこのtopicのAcrobat Readerで表示されているページ番号は2ではなくiです.
これは最初の1161 0オブジェクトでページラベルが
<< /Nums [ 0 << /S /D >>
1 << /S /r >>
7 << /S /D >> ] >>
1 << /S /r >>
7 << /S /D >> ] >>
すなわち、
0ページから D Decimal arabic numerals
1ページから r Lowercase roman numerals
7ページから R Uppercase roman numerals
1ページから r Lowercase roman numerals
7ページから R Uppercase roman numerals
としてあるからです.これにより2ページ目(0から数えれば1ページ目)はi,ii,iiiの形式、すなわち「i」がページ番号ということがようやくわかります.
つまり、Named Destinationの値がわかればPDFの内部構造を追うことにより、それが何ページかは取得できそうです.
これをどうやって組み込むかですが、DITA⇒XSL-FOのスタイルシートでJavaのXSLTプロセッサ(DITA-OTの場合はSaxon 9)から呼び出すJavaのライブラリ静的関数として実装すればよいでしょう.
たとえばこの例の場合だとPDFファイルのパスと"topic_14F0A9302584C94D"というNamed Destinationの値を渡して"i"という文字列を得るという感じになります.これで
For more detail, see "XXXX" on page NN in Technical guide.
のNNの部分は"i"に置き換えることができます.
しかしPDFからページ番号を得ることはできても、あまり実用的ではないかもしれません.なぜならPDFからはページ番号を得ることはできてもtopicのタイトル文字列を得ることは容易ではないからです.つまりリンク元のrelated-links(reltable)でも参照先のタイトル("XXXX")は手入力しなければなりません.
でもそこまでやってもページ数が得たいという場合にはこのようなライブラリでも作る意味はあるでしょう.