はじめに
この記事は、あくあたん工房 Advent Calendar 2022の14日目の記事です。
初めて記事を書くので、体裁等が読みづらいかと思いますが、ご了承ください。
きっかけ
普段何気なく使っているPDFですが、どのようにデータを管理しているのでしょうか?
この記事は、細かいことは別にいいけど、どんな感じかだけ知りたい人を対象にしております。
ので、正確に読みたい人はリファレンスをどうぞ。
とにかくしのごの言わずに、始めますね
対象
今回扱うPDFファイルは、以下のLaTeXをLuaLaTeXでコンパイルしたものです。
\documentclass[a4paper,11pt]{ltjsarticle}
\begin{document}
Hello World.
\end{document}
中身
とりあえず中身を見てみましょう。
うわあああああぁぁぁぁぁぁぁぁぁぁ!!!!▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂
文字化けがすごいな…
とりあえず、読めるところだけ抜き出してみましょう。
ふむφ(・ω・ )
なんか<<から>>までがブロックになってそうやな
もうちょい見やすくしてみるか
%PDF-1.5
3 0 obj
<<
/Filter /FlateDecode
/Length 112
>>
stream
// 文字化けしてたとこ
endstream
endobj
9 0 obj
<<
/Filter /FlateDecode
/Length 21
>>
stream
// 文字化けしてたとこ
endstream
endobj
10 0 obj
<<
/Subtype /CIDFontType0C
/Filter /FlateDecode
/Length 1269
>>
stream
// 文字化けしてたとこ
endstream
endobj
11 0 obj
<<
/Filter /FlateDecode
/Length 407
>>
stream
// 文字化けしてたとこ
endstream
endobj
14 0 obj
<<
/Producer (LuaTeX-1.15.0)
/Creator (TeX)
/CreationDate (D:20221213205749+09'00')
/ModDate (D:20221213205749+09'00')
/Trapped /False
/PTEX.FullBanner (This is LuaHBTeX, Version 1.15.0 (TeX Live 2022))
>>
endobj
6 0 obj
<<
/Type /ObjStm
/N 8
/First 47
/Filter /FlateDecode
/Length 500
>>
stream
// 文字化けしてたとこ
endstream
endobj
15 0 obj
<<
/Type /XRef
/Index [ 0 16 ]
/Size 16
/W [ 1 2 1 ]
/Root 13 0 R
/Info 14 0 R
/ID [ <C276673F1AFB1BCA87C4572C62F8DF7D> <C276673F1AFB1BCA87C4572C62F8DF7D> ]
/Filter /FlateDecode
/Length 60
>>
stream
// 文字化けしてたとこ
endstream
endobj
startxref
3006
%%EOF
読み解く
ふむふむφ(・ω・ )
とりあえず、以下の書式が連続してるみたいですね
``{n}はなんかの数字``
{n} {n} obj
<<
なんか設定っぽいところ
>>
stream
文字化けしてたところ
endstream
endobject
あと全体的には、
%PDF-1.5
オブジェクト的なやついっぱい
startxref
{n}
%%EOF
こんな感じですね。
末尾のstartxrefってなんや?下の数字(今回だと3006)も気になる…
3006文字目に何かあるのでしょうか?でも3006文字もなさそうです
これは( ゚д゚)ハッ!3006バイト目か!
早速調べてみると
endobj
ここ
|
V
15 0 obj
<<
/Type /XRef
/Index [ 0 16 ]
/Size 16
/W [ 1 2 1 ]
/Root 13 0 R
/Info 14 0 R
/ID [ <C276673F1AFB1BCA87C4572C62F8DF7D> <C276673F1AFB1BCA87C4572C62F8DF7D> ]
/Filter /FlateDecode
/Length 60
>>
stream
// 文字化けしてたとこ
endstream
endobj
startxref
3006
%%EOF
15 0 obj
の1
が3006バイト目でした。おそらくここが重要なブロックなのでしょう。
よく見れば/Type /XRef
とあります。
その中で、次に気になるのが、/Filter /FlateDecode
のDecode
の文字
FlateDecode
でググると、Deflateにあたりました。まさか、圧縮されてます???
見えるぞ!私にもPDFが見える!
文字化けの原因は圧縮にあったようです。採用されている圧縮方法はそこまで難しいアルゴリズムではないようなので、自分で作れそうですが、今回はツールに頼りましょう。使ったツールはPDFtkの無料版です。
インストール後、以下のコマンドをたたくと、展開したPDFが生成されます。
pdftk HelloWorld.pdf output HelloWorld_uncompress.pdf uncompress
中身をすべて貼ると、なかなかに長いので、「Hello World.」に結びつくところを載せておきます。
%PDF-1.5
%����
1 0 obj
<<
/Font
<<
/F19 2 0 R
>>
/ProcSet [/PDF /Text]
>>
endobj
3 0 obj
<<
/Parent 4 0 R
/MediaBox [0 0 595.276 841.89]
/Resources 1 0 R
/pdftk_PageNum 1
/Contents 5 0 R
/Type /Page
>>
endobj
5 0 obj
<<
/Length 146
>>
stream
BT
/F19 10.9091 Tf
1 0 0 1 85.794 718.818 Tm [<003E0032004800480051>-333<0071>83<005100600048002F0058>]TJ
1 0 0 1 294.911 61.993 Tm [<0052>]TJ
ET
endstream
endobj
2 0 obj
<<
/DescendantFonts [6 0 R]
/BaseFont /SIIWUI+LMRoman10-Regular
/Subtype /Type0
/ToUnicode 7 0 R
/Encoding /Identity-H
/Type /Font
>>
endobj
4 0 obj
<<
/Kids [3 0 R]
/Count 1
/Type /Pages
>>
endobj
8 0 obj [47 [556] 50 [444] 62 [750] 72 [278] 81 [500 500] 88 [278] 96 [392] 113 [1028]]
endobj
9 0 obj
<<
/FontName /SIIWUI+LMRoman10-Regular
/StemV 93
/FontFile3 10 0 R
/Ascent 1127
/Flags 4
/XHeight 431
/Descent -290
/ItalicAngle 0
/CIDSet 11 0 R
/FontBBox [-430 -290 1417 1127]
/Type /FontDescriptor
/CapHeight 683
>>
endobj
11 0 obj
<<
/Length 15
>>
stream
~~ 中略 ~~
endstream
endobj
7 0 obj
<<
/Length 804
>>
stream
~~ 中略 ~~
>> def
/CMapName /TeX-Identity-SIIWUI-LMRoman10-Regular
def
/CMapType 2
def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
9 beginbfchar
<002F> <0064>
<0032> <0065>
<003E> <0048>
<0048> <006C>
<0051> <006F>
<0052> <0031>
<0058> <002E>
<0060> <0072>
<0071> <0057>
endbfchar
endcmap
CMapName currentdict
/CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
6 0 obj
<<
/BaseFont /SIIWUI+LMRoman10-Regular
/CIDSystemInfo
<<
/Supplement 0
/Ordering (Identity)
/Registry (Adobe)
>>
/Subtype /CIDFontType0
/FontDescriptor 9 0 R
/W 8 0 R
/Type /Font
>>
endobj
12 0 obj
<<
/Pages 4 0 R
/Type /Catalog
>>
endobj
~~ 中略 ~~
trailer
<<
/Info 13 0 R
/Root 12 0 R
/Size 14
/ID [<c276673f1afb1bca87c4572c62f8df7d> <c276673f1afb1bca87c4572c62f8df7d>]
>>
startxref
3915
%%EOF
おぉ!(゚ロ゚屮)屮
一部文字化けしているところはありましたが、大半が読めるようになりました。
テキスト情報はどこ?
ここからは、「Hello World.」への道筋を説明します。
末尾にあるtrailer
情報から/Root
をみつけます。
trailer
<<
/Info 13 0 R
/Root 12 0 R
/Size 14
/ID [<c276673f1afb1bca87c4572c62f8df7d> <c276673f1afb1bca87c4572c62f8df7d>]
>>
今回だと 12 0 R
です。ここから、12 0 obj
を探します。同じ様にたどっていくと、
12 0 obj
-> 4 0 obj
-> 3 0 obj
-> 5 0 obj
にたどり着くことができます。
5 0 obj
<<
/Length 146
>>
stream
BT
/F19 10.9091 Tf
1 0 0 1 85.794 718.818 Tm [<003E0032004800480051>-333<0071>83<005100600048002F0058>]TJ
1 0 0 1 294.911 61.993 Tm [<0052>]TJ
ET
endstream
endobj
[<003E0032004800480051>-333<0071>83<005100600048002F0058>]
がHello World.っぽい。ただなんだこれ?
謎コードの羅列はフォントのせい
そこで、3 0 obj
->1 0 obj
の方をたどってみましょう。
3 0 obj
->1 0 obj
->2 0 obj
->7 0 obj
7 0 obj
<<
/Length 804
>>
stream
~~ 中略 ~~
>> def
/CMapName /TeX-Identity-SIIWUI-LMRoman10-Regular
def
/CMapType 2
def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
9 beginbfchar
<002F> <0064>
<0032> <0065>
<003E> <0048>
<0048> <006C>
<0051> <006F>
<0052> <0031>
<0058> <002E>
<0060> <0072>
<0071> <0057>
endbfchar
endcmap
CMapName currentdict
/CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
このstream内のdef以降が使用するフォント情報になっています。特に、
9 beginbfchar
<002F> <0064>
<0032> <0065>
<003E> <0048>
<0048> <006C>
<0051> <006F>
<0052> <0031>
<0058> <002E>
<0060> <0072>
<0071> <0057>
endbfchar
の部分が使用する文字コードとASCIIコードの対応表になっています。つまり、
{変換する数} beginbfchar
<使用するコード> <対応するASCIIコード>
endbfchar
そしてHello World.へ
この対応表を元に、先ほどの[<003E0032004800480051>-333<0071>83<005100600048002F0058>]
を変換してみましょう
[<003E0032004800480051>-333<0071>83<005100600048002F0058>]
| (対応表で変換)
V
[<00480065006C006C006F>-333<0057>83<006F0072006C0064002E>]
| (ASCIIコードから変換)
V
[(Hello)-333(W)83(orld.)]
キタ――(゚∀゚)――!!
ちなみに間に挟まっている数字は、文字の位置を調整しています。(-333で半角スペース分移動させて、83でo以降をWに寄せている。)
最後に
Hello World.を見つけるだけでも結構な手間がかかることがわかりますね。(人がやることじゃねぇ…)
今回読み解いたPDFのバージョンは1.5ですが、現行は1.7であり、互換性があるため、1.7でも正常に動作すると思われます。
また、今回は概略を書きましたが、テキストの位置の決め方や画像データのとり方なども今後タイミングがあれば書いてみようと思います。