PDFのよみかた

3.5K Views

August 10, 25

スライド概要

ShizuokaTECH#1

profile-image

yak shaving に喉をつまらせる日々を送っています

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

PDFのよみかた ShizuokaTECH#1 Shotaro Hirukawa(@shotanue) 1

2.

自己紹介 • 蛭川尚太郎(@shotanue) • 富士宮から来ました • 弁護士ドットコム株式会社 ◦ 育休中 ◦ フロントエンドエンジニア(自称) ▪ (直近はひたすらTerraformとGoを書いてました) ◦ フルリモート • MAZDAロードスター 2

3.

PDFのよみかた とは 3

4.

PDF(のコンテンツを)読む? よくあると思います 4

5.

PDF(をプログラムで扱う対象 として)読む まれによくあると思います 5

6.

ライブラリ探しの旅 6

7.
[beta]
とあるPDFライブラリのサンプルコード
// https://github.com/J-F-Liu/lopdf/blob/c9a44ec72e80a9d7096cd56fabfae02ac18d0e96/README.md
use lopdf::{Document, Object, Stream, dictionary};
use lopdf::content::{Content, Operation};
let mut doc = Document::with_version("1.5");
let pages_id = doc.new_object_id();
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Courier",
});
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! {
"F1" => font_id,
},
});
let content = Content {
operations: vec![
Operation::new("BT", vec![]),
Operation::new("Tf", vec!["F1".into(), 48.into()]),
Operation::new("Td", vec![100.into(), 600.into()]),
Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
Operation::new("ET", vec![]),
],
};
let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
7
8.

• Object...? • Operation...? • Stream...? 8

9.

リテラシーを求められる 9

10.

PDFを読めるようになればいいじゃないの(?) 10

11.

PDFとは 11

12.

PDF = Portable Document Format %PDF-1.7 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream xref 0 5 0000000000 65535 f 0000000015 00000 n 0000000074 00000 n 00000000173 00000 n 00000000301 00000 n trailer << /Size 5 /Root 1 0 R >> startxref 380 %%EOF 12

13.

テキストとバイナリが混在したファイルフォーマット エディタで開けます 13

14.

``` %PDF-1.7 ``` ↓↓↓↓↓↓ 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream ``` ``` xref 0 5 0000000000 65535 f 0000000015 00000 n 0000000074 00000 n 00000000173 00000 n 00000000301 00000 n ``` trailer << /Size 5 /Root 1 0 R >> startxref 380 %%EOF ``` 14

15.

Object 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream 15

16.
[beta]
Object
1 0 obj
<< /Type /Catalog /Pages 2 0 R >> # {Type: "Catalog", Pages: "2 0 R"}
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >> # {Type: "Pages", Kids: ["3 0 R"], Count: 1}
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /Contents 4 0 R >> # {Type: "Page", Parent: "2 0 R", Contents: "4 0 R"}
endobj
2 0 Rや3 0 Rはオブジェクトの参照
16
17.

Stream 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream コンテンツを格納 命令やバイナリを含む 17

18.

手続型のような記述 4 0 obj << /Length 44 >> stream BT ← テキスト描画開始 /F1 12 Tf ← フォントF1、サイズ12を設定 72 720 Td ← 座標(72,720)に移動 (Hello!) Tj ← 文字列"Hello!"を描画 ET ← テキスト描画終了 endstream 上から順番に実行される 18

19.
[beta]
Stream(バイナリ)
5 0 obj
<<
/Type /XObject
/Subtype /Image
/Width 100
/Height 100
/Length 1024
>>
stream
ÿØÿàJFIFHhÿÛC
ÿÄ@÷}!1AQa"q2#BR¡±ÁÑðñ$3br
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º
endstream
19
20.

参照を解決すると右のような木構造に 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream 1 0 obj カタログ /Type /Catalog /Pages 2 0 R 2 0 obj ページツリー /Type /Pages /Kids 3 0 R 3 0 obj ページ /Type /Page /Contents 4 0 R 4 0 obj コンテンツ Hello!の描画命令 20

21.

``` %PDF-1.7 ``` ``` 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream ``` ``` ↓↓↓↓↓↓ xref 0 5 0000000000 65535 f 0000000015 00000 n 0000000074 00000 n 00000000173 00000 n 00000000301 00000 n ``` ``` trailer << /Size 5 /Root 1 0 R >> startxref 380 %%EOF ``` 21

22.

XRef(クロスリファレンス) xref 0 5 0000000000 65535 f 0000000015 00000 n ← オブジェクト1は15バイト目 0000000074 00000 n ← オブジェクト2は74バイト目 00000000173 00000 n ← オブジェクト3は173バイト目 00000000301 00000 n ← オブジェクト4は301バイト目 仕組み: バイトオフセットでO(1)アクセス • オブジェクト1を取得したい→ XRefで位置検索 → 直接 ジャンプ • PDFはランダムアクセスに強い設計になっている 22

23.

``` %PDF-1.7 ``` ``` 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream ``` ``` xref 0 5 0000000000 65535 f 0000000015 00000 n 0000000074 00000 n 00000000173 00000 n 00000000301 00000 n ``` ``` ↓↓↓↓↓↓ trailer << /Size 5 /Root 1 0 R >> startxref 380 %%EOF ``` 23

24.

trailer trailer ← トレーラ << /Size 5 /Root 1 0 R >> startxref 380 %%EOF ルートノードの参照IDやクロスリファレンスのオフセット 位置を示す 24

25.

ここまでくるとPDFが読めると言っても過言ではない 25

26.

PDFのよみかた %PDF-1.7 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Contents 4 0 R >> endobj 4 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello!) Tj ET endstream xref ← クロスリファレンス 0 5 0000000000 65535 f 0000000015 00000 n 0000000074 00000 n 00000000173 00000 n 00000000301 00000 n trailer ← トレーラー << /Size 5 /Root 1 0 R >> startxref 380 %%EOF 1. トレーラーをファイル末尾から 取得する 2. トレーラーから以下を取得する ◦ ルートノードの参照ID ◦ クロスリファレンスのオフ セット位置 3. ルートノードの参照IDを起点に PDFの木構造を探索する 26

27.
[beta]
ライブラリのI/Fも読めるような気がしてきませんか
use lopdf::{Document, Object, Stream, dictionary};
use lopdf::content::{Content, Operation};
let mut doc = Document::with_version("1.5");
let pages_id = doc.new_object_id();
// 意味がわかるようになる!
let font_id = doc.add_object(dictionary! { // ← オブジェクト辞書の作成
"Type" => "Font", // ← PDF仕様のType/Subtype
"Subtype" => "Type1",
"BaseFont" => "Courier",
});
let resources_id = doc.add_object(dictionary! { // ← リソース辞書
"Font" => dictionary! { "F1" => font_id, } // ← フォント参照
});
let content = Content { // ← コンテンツストリーム
operations: vec![ // ← PDF描画オペレーター
Operation::new("BT", vec![]), // ← Begin Text
Operation::new("Tf", vec!["F1".into(), 48.into()]), // ← Text Font
Operation::new("Tj", vec![Object::string_literal("Hello!")]),
Operation::new("ET", vec![]), // ← End Text
],
};
let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
27
28.

参考 『詳細PDF入門』 - itchyny https://itchyny.hatenablog.com/ entry/2015/09/16/100000 『PDF構造解説』 - O'Reilly 180ページと意外にコンパクト 28

29.

面白いと思った方は チャンネル登録(?)と高評価(?) よろしくお願いします(?) X: @shotanue 29