vivlio-starter-pdf 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -10
- data/README.md +44 -30
- data/RELEASE_NOTE.md +60 -101
- data/exe/vivlio-starter-pdf +89 -31
- data/lib/vivlio_starter/cli/pdf/enhanced_provider.rb +10 -85
- data/lib/vivlio_starter/cli/pdf/outline_writer.rb +54 -12
- data/lib/vivlio_starter/cli/pdf/reader.rb +856 -232
- data/lib/vivlio_starter/cli/pdf/version.rb +1 -1
- data/vivlio-starter-pdf.gemspec +14 -19
- metadata +55 -20
- data/Rakefile +0 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8187410639d9f7484e68a77119823e90759c86c133a296815ff442450f3b931
|
|
4
|
+
data.tar.gz: 75ffb4e851f72af947282d8ebf16dc7de2c8b3aa67fbb44b8024856a969ae5d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0e4775ead10926f65f758196c821d68738ef383096b6582a6321e21866e86b55e936e15dcbbf9e12c0783a2a3c56de04021623b4572ff6d319a687559b1fbbdb
|
|
7
|
+
data.tar.gz: 2d64a45788c9f2663f5f0046334d38b62fc022f21366ed70c9a527ee29092822f59c6cc55b3ac7a3d6d8ee7fef709a634afce9b41ec0d24070c1dee025573744
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,79 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Planned
|
|
11
|
+
- パフォーマンス最適化(大規模 PDF 対応)
|
|
12
|
+
- 追加 OCR エンジン対応
|
|
13
|
+
- クラウド OCR サービス連携
|
|
14
|
+
- PDF 暗号化対応
|
|
15
|
+
|
|
16
|
+
## [1.2.0] - 2026-11-01
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **しおりの項目が `:top` を持つとき、見出しの位置まで飛ぶようにした** (`outline_writer.rb`・本体 `pdf-outline-heading-id-spec.md` R7): 本体は節と項の項目に、見出しの上端の y(PDF の座標)を `:top` として渡す。`[ページ, /XYZ, null, top, null]` を書くので、左端と倍率は読者のまま変わらない。`:top` が無い項目(章。章扉は見出しの上に扉絵がある)は、今までどおりページ全体を見せる。`:top` がページの外を指すときや数でないときは、項目を落とさずページ全体に戻す。古い本体は `:top` を渡さないので、今までと同じに動く。
|
|
20
|
+
- **`read` に、読むページを絞る設定 `pages` を足した** (`reader.rb` の `read_page?`・本体 `pdf-read-file-naming-spec.md` §10): `pages=[31,32,52]` のように 1 から数えたページを渡すと、ほかのページは文字も画像も OCR も取らず、結果の配列には空の値を置く(ページの添字はそろえたまま)。`vs pdf:read --pages` が、指定のページと柱を見分ける前後のページだけを OCR にかけ、大きな本の一部を読むときの時間を省くために使う。OCR の経過は、読むページのうちの何ページ目かで知らせる。
|
|
21
|
+
- **白地の多い絵を、枠を削りすぎずに図にするようにした** (`reader.rb` の `figure_boxes`・`pdf-read-ocr-improvement-notes.md` §4.17): 図の枠は、周りの字のかけらを外すために「埋まった列・行」へ削るが、白地の多い絵は埋まった列・行がまばらで、削ると下限より小さくなって捨てられていた(97 章の光格子時計の絵が 130×78pt から 48×18 画素に)。削った枠が図の条件を満たさないとき、削る前の枠が満たせばそちらを採る。97 章のスキャン(印刷「普通」)で図は 13 → 14 枚になり、ほかに増えた図は無い。
|
|
22
|
+
- **斜体の行を、傾きを戻して読み直すようにした** (`reader.rb` の `reread_slanted_lines`・`pdf-read-ocr-improvement-notes.md` §4.15): 和文の斜体は字を傾けて描かれ、Tesseract は意味の無い英字(確信度 0〜40)や崩れた和文として読む。確信度の平均が 75 未満で 8 字以上の行を、行の高さぶんの余白を付けて切り出し、12° 逆に傾けて読む。確信度 80 以上の字が 4 字以上、半分以上が和文で、もとの読みより十分に多く、さらに同じ所を傾けずに切り出した読みよりも十分に多いときだけ置き換える(和文の文字列を含むコードの行を崩れた読みで置き換えないため)。97 章のスキャンで候補 57 行のうち引用の 11 行が置き換わり、F 値は 91.7% → 92.7%(印刷「普通」)、92.4% → 93.5%(「最高」)。
|
|
23
|
+
- **スキャンの傾きを直すようにした** (`reader.rb` の `skew_angle`・`straighten`・`pdf-read-ocr-improvement-notes.md` §4.14): 縮めた画像の濃い画素を 0.05° 刻みで ±3° まで回し、行ごとの和の二乗和が最大の角度を傾きとする(1 ページ 0.2 秒)。読み取りは傾いたままの画像で行い、TSV の枠の位置だけを回す(回した画像を読ませると字がにじみ、英字の語が 212 → 184 に落ちた)。行の枠は傾いた行を囲んだぶん縮める。図はまっすぐにした画像から見つけて切り出すので、縁に三角の余白が付かない。
|
|
24
|
+
- **Tesseract が読み飛ばした表や色の帯の中の見出しを、切り出して読み直すようにした** (`reader.rb` の `reread_skipped_regions`・`pdf-read-ocr-improvement-notes.md` §4.11): ページ全体を読むと、Tesseract は先に紙面の割り付けを推定し、罫線で囲んだ表を文字の無いブロックとして読み飛ばす。語の無いブロックのうち接し合うものを 1 つにまとめ、余白を付けて切り出して読み直す。確信度 80 以上の字が 4 字以上で半分以上を占め、もとの読みより多いときだけ、その範囲の語を置き換える。すでに読めた語が 1 割を超えて覆う範囲(囲みの中の文・コード)と、図の範囲は読み直さない。97 章のスキャンで、表 H-1 と SI 単位の表、色の帯の見出しが読めるようになり、F 値は 90.5% → 90.7%(印刷「普通」)。時間は 24 ページで数秒増える。
|
|
25
|
+
- **スキャンしたページの図版の検出を作り直した** (`reader.rb` の `extract_illustration_regions_vips`・`pdf-read-ocr-improvement-notes.md` §4.2・§4.4): これまでは行ごと・列ごとに画像を切り出して濃さを測り、97 章の図 15 枚のうち 1 枚しか見つけられず、1 ページ約 3 秒かかっていた。ページを幅 310px に縮め、紙より濃い画素と色の付いた画素から、OCR で読めた文字(確信度 60 以上の語と行)を消し、残ったまとまりを図とみなす。枠は膨らませる前の画素で取り、埋まった列・行の連なりへ削る(写真の横の本文にかからない)。ページの端に接するもの(飾りの帯・色を敷いた章扉)と、辺 40pt・面積 6,000pt² 未満のもの(見出しの色の札・アイコン)は図にしない。97 章のスキャンで図は 14 枚見つかり、誤りは 0。24 ページの読み取りは 175 秒 → 108 秒(印刷「最高」)、181 秒 → 101 秒(「普通」)になった。白地の線だけの図は見つけられない。テストの見本(`test/fixtures/scanned.pdf`)の青い四角も図として切り出すようになったので、その統合テストの期待を「ページ全面のスキャンは出さず、ページの中の図だけを出す」に改めた。
|
|
26
|
+
- **英字の多いページだけ、英語の学習データでも読み直し、英字の語を語ごとに採るようにした** (`reader.rb` の `english_reading`・`pdf-read-ocr-improvement-notes.md` §4.6): 日本語+英語で読むと、コードの `@param` を `Gparam`、`velocity` を `vetoctty` と読み違える。英語だけで読むとコードはよく読めるが、和文はすべて崩れる。そこで英字の語が 20 以上あるページだけ `-l eng` でもう一度読み、日本語+英語の読みの行ごとに、英字の語の連なり(和文の語にはさまれた範囲)へ中心の入る英語の読みの語を当てはめる。2 つの読みで食い違う語について、英語の読みの確信度の平均が 60 以上で、日本語+英語の読みより高いときに差し替える。和文の語はそのまま残す。差し替えた語は元の行の番号を引き継ぎ、枠は連なりの範囲に収める(はみ出すと隣の和文の語との空白が消える)。97 章のスキャンで、英字の語は 205 → 212(印刷「最高」・600dpi を縮めたもの)、195 → 205(「普通」・300dpi)になった。読み直すのは 24 ページ中 6〜8 ページ。
|
|
27
|
+
- **解像度の高すぎるスキャンを、300dpi に縮めてから読むようにした** (`reader.rb` の `downscale_for_ocr`): ページの幅から見積もった解像度が 400dpi を超える埋め込みの画像は、vips で 300dpi へ縮め、JPEG(品質 92)で書いてから Tesseract に渡す。Tesseract は字が大きすぎても読み違える。97 章を 600dpi で読むと F 値 89.3%・5 分 14 秒、縮めると 90.6%・3 分 5 秒だった。ページを実物より大きく作った PDF(見かけの解像度が低く出る)は縮める側に当たらない。
|
|
28
|
+
- **囲みや絵の多いページだけ、二値化した画像でも読み、確信度の高い字の多いほうを採るようにした** (`reader.rb` の `second_reading`・`pdf-read-ocr-postprocess-spec.md` §2.2): ページ全体にいつも二値化をかけると悪くなるが、地の色の付いた囲みのページでは大きく効く。vips で明るさの分布を取り、紙の明るさより 10〜60 暗い面(囲みの地)が 8% 以上のページだけ、大津の二値化(しきい値は同じ分布から求める)でも Tesseract を走らせる。選ぶ基準は確信度の平均ではなく、確信度 80 以上の語に含まれる字の数(平均で選ぶと、絵の本で 416 字の読みを捨てて 361 字の読みを選ぶページがあった)。スキャンした 97 章では 24 ページ中 5 ページが二度読みになり、文字の一致(F 値)が 0.9〜1.2 ポイント上がる。時間の増加は約 10 秒。
|
|
29
|
+
- **`read` に `progress=true` を足した** (`exe/vivlio-starter-pdf`): OCR するページごとに標準エラーへ `progress: ocr 5/24` と書く。本体が `--log` のときに中継する。
|
|
30
|
+
- **`read` の結果に、OCR で読んだ語の位置(`page_words`)とページの大きさ(`page_sizes`)を足した** (`reader.rb`): `page_words` はページごとの配列で、OCR したページなら語の一覧(`text`・`x`・`y`・`width`・`size`・`conf`。座標は PDF のページの pt で、`y` はベースライン、`size` は行の高さ)、しなかったページなら `null`。本体の `vs pdf:read` はこれを字に割り、文字の層のある PDF と同じ紙面の推定(`pdf-read-structure-spec.md` の経路 B)で見出し・段落・コードを戻す。ベースラインと大きさは語ではなく Tesseract の行(level 4)の枠から取る(語の枠は句読点や小書きの字で上下し、同じ行の語がそろわない)。これまでの `page_texts`・`page_chunks` は残すので、語を読まない本体でも動く。
|
|
31
|
+
- **実行ファイルに `read` サブコマンドを足した** (`exe/vivlio-starter-pdf`): `vivlio-starter-pdf read PDF key=value …` で PDF を読み、`Reader#execute` の結果を JSON で標準出力へ書く。本体の `vs pdf:read` は Enhanced Mode でこの形を呼んでいたが、**実行ファイルは `--version` と `--help` しか知らず**、`Unknown option 'read'` で終わっていた。git 履歴を通して `read` は一度も無く、**Enhanced Mode は `vs` から一度も動いていなかった**(本体のテストは呼び出しをスタブしていた)。設定は本体が渡す 6 つ(`page_separator`・`text_area`・`line_merge_tolerance`・`images_dir`・`image_reference_dir`・`ocr`)を受け、知らないキーは黙って捨てずに止める。標準出力は JSON だけにし、誤りは標準エラーへ出す。実行ファイルを子プロセスで起動するテスト(`test/exe_test.rb`)を足した。
|
|
32
|
+
|
|
33
|
+
### Removed
|
|
34
|
+
- **インストールの後の案内(`post_install_message`)を外した** (`vivlio-starter-pdf.gemspec`): OCR に要る外部ツール(tesseract・tesseract-lang・poppler・vips)と `vs doctor --fix` を案内していた。本体の導入スクリプト(`bin/install.zsh`)がこの gem を一緒に入れるようになり、ツールも同じ導入の中の `vs doctor --fix` が入れる。導入の途中に「必要です」と出ても、著者にはすることが無い。別に入れた人にも、本体の `vs doctor --fix`(`vs new` の最後にも走る)が OCR のツールを入れる。
|
|
35
|
+
- **隠しノンブルの書き込み(`stamp_nombre!`)を削除した** (`enhanced_provider.rb`): 本体はすでに Prawn + `qpdf --overlay` の MIT 共通実装へ移しており、**プラグインのこのメソッドはどこからも呼ばれていなかった**。139 行のうち 92 行が到達不能なまま残り、テスト 2 件がそれを検証して緑を出していた——動かない機能を確かめる緑は、あるほうが害になる。
|
|
36
|
+
|
|
37
|
+
本体が `Pdf.provider` に委ねるのは `page_count` / `ensure_blank_page_pdf` / `add_outline!` の 3 つだけである。ノンブルを戻す予定は無い。プラグインの有無で成果物が変わらないほうがよい種類の処理で、CombinePDF が named destinations を再構築しない問題もそれで解消している。公開 API が 1 つ減るので SemVer なら major だが、**1.2.0 とした**。呼び出しうるのは本体 `vivlio-starter` だけで、その本体はすでに呼んでいない。本体が 1.0.0 を迎えるのに合わせて配るため、プラグインだけが 2.0.0 になる並びを避けた。
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- **OCR の誤認識補正が 2026-08-04 から一度も効いていなかったのを直した** (`reader.rb`): 補正表の読み込み先が `config/textlint_prh.yml` と決め打ちで、本体側の `6bbc2007`「校正まわりの設定ファイル名を対にする」でこのファイルが `textlint_rewrite.yml` へ改名された際に取り残されていた。`File.file?` が false を返すので例外も警告も出ず、**空の表を当てて素通りする**——気付けない形で壊れていた。`vs pdf:read` の統合テストが「ログラミング技術習得の三要素」(先頭の「プ」が落ちたまま)を期待していたのが、動いていなかった証拠である。
|
|
41
|
+
- **HexaPDF が書けない画像を pdfimages で書き出し、白地の多い絵の枠を削りすぎないようにした** (`reader.rb` の `write_extracted_image`・`poppler_page_images`・`bridge_sparse`・`pdf-read-ocr-improvement-notes.md` §4.20): HexaPDF は色の表(Indexed)を別のオブジェクトに置いた画像の情報を読めずに例外を出し、`fundamentals-of-system-design.pdf` の回路図などが 103 個中 9 個しか取れなかった。書けない画像は `pdfimages` でページの画像を書き出し、`-list` のオブジェクトの番号で選ぶ(103 個すべて取れる)。また、図の枠を埋まった行の連なりへ削るとき、区画の境目にはさまる少しだけ埋まった行(1 割以上・8 本以内)で切っていたので、白地の多い絵や表紙の一部しか切り出せなかった。そうした行は続きとみなす(97 章の光格子時計の絵と『量子力学』の表紙が全体で取れる。ほかの図は変わらない)。
|
|
42
|
+
- **OCR を切る値 `disable` を受け付けるようにし、読めない命令のあるページでも読み続けるようにした** (`reader.rb` の `normalize_ocr_mode`・`PageTextCollector#process`・`pdf-read-ocr-improvement-notes.md` §4.19): 本体と `book.yml` は OCR を切る値を `disable` と書くが、`never`・`false`・`off` しか知らず `auto` にしていた(最初のコミットから)。また、資源に無い色空間(dvipdfmx の `/CS0 cs`)や縦書きの字で HexaPDF が例外を出すと、ページの文字と画像の位置を丸ごと捨てていた。2 つが重なり、文字の層のある PDF の全ページを OCR して、探究 Rust プログラミング(392 頁)・データ解析に強くなる Rust レシピ(313 頁)が 15 分を超えても終わらなかった。`disable`・`disabled` を `:never` にし、失敗した命令だけを飛ばす。2 冊は 5.7 秒・7.1 秒で終わり、画像は 3 枚・38 枚取れる。
|
|
43
|
+
- **文字を 1 字ずつ描く PDF で、各行が「全文+先頭の 1 字を欠いた全文」に二重化していた** (`reader.rb` の `PageTextCollector#build_lines`): 行の組み立てが `buffer = +fragment.text` で始まっていた。`+` は凍結されていない文字列を複製せずに返すので、続く `<<` が先頭の断片の文字列そのものを行全体へ書き換える。`extract_page_content` は `text` と `lines` で組み立てを 2 回呼ぶため、2 回目の各行が膨らんでいた。Chrome(Vivliostyle)の PDF は字ごとに断片を描くので、`vs build` の PDF では全行がこうなる。最初のコミットからあった。Prawn で作る fixture は 1 行を 1 断片で描くため、通し経路のテストでは表に出なかった。複製して受けるようにし、複数断片の行を 2 回組み立てるテストを足した。
|
|
44
|
+
- **統合テスト 2 件が skip されたままになっていた** (`test/reader_test.rb`): fixture が隣の repo(`../vivlio-starter/sources/*.pdf`)を指していたが、その PDF は `.gitignore` の対象で git に入っておらず、開発機からも無くなっていた。`File.exist?` が false を返すとテストは skip され、失敗として報告されないので、**この gem の中核である `reader.rb`(1,262 行)の通し経路は未検証のまま**だった。この版で直した OCR 補正の不具合(補正表の読み込み先)も、それを捕まえるはずのテストが止まっていた。
|
|
45
|
+
|
|
46
|
+
fixture を `test/fixtures/` へ同梱し、組み立て手順を `build_fixtures.rb` に残した。読む側が HexaPDF なので、作る側は Prawn にしてある——同じ実装で作って読むと、その実装の癖が打ち消し合って検証から外れる。OCR 用には文字レイヤーを持たない PDF(`scanned.pdf`)を焼いた。テストは **34 runs / 2 skips → 37 runs / 0 skips** になった。
|
|
47
|
+
|
|
48
|
+
- **Minitest 6 でテストが起動しなくなる状態だった** (`test/test_helper.rb`): `Object#stub` を使いながら `minitest/mock` を読んでいなかった。5 は autorun がついでに読むので通り、6 は `Dropped minitest/mock.rb. This has been extracted to the minitest-mock gem.`(History.rdoc)で読まれない。`bundle exec` 有無で結果が変わる状態でもあった。明示的に require し、`minitest-mock` を開発依存へ加えた。
|
|
49
|
+
|
|
50
|
+
- **正規表現の重複範囲の警告が 5 件出ていた** (`reader.rb`): `々` は `p{Han}`、`ヵ` `ヶ` は `p{Katakana}` と `ァ-ヶ` に含まれる。判定は変わらない(全コードポイントで照合して確認)が、毎回 5 行の警告が出ると警告そのものを読み飛ばすようになる。
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- **スキャンした PDF の OCR を約 4 倍速くした** (`reader.rb`): ページ全体を覆う埋め込みの画像(JPEG・PNG)があれば、`pdftoppm` でページを描き直さず、その画像を Tesseract へ渡す。描き直しは 300dpi・A4 の JPEG 1 枚で 23 秒かかり、読み取りの時間のほぼすべてだった(書き出しなら 0.05 秒)。24 ページのスキャンで 624 秒 → 151 秒、読み取りの結果は変わらない(原稿との一致の F 値 88.7% → 88.6%)。解像度はページの寸法からではなく画像の幅で見る。ページを実物より大きく作ったスキャン PDF(1542×2048pt に幅 2,057px)を設定の dpi で描き直すと、情報の増えない 3 倍の引き伸ばしになるため。幅が 1,600px に届かない画像は vips で拡大してから読む(幅 875px で確信度 88.8 → 92.2。300dpi の描き直しと同等で 0.5 秒)。回転の指定があるページ、縦横比が合わない画像、vips が無いときの小さな画像は、これまでどおり描き直す。`pdf_read.ocr.dpi` は描き直すページにだけ効く。あわせて Tesseract を 1 ページ 1 回の起動にした(これまでは TSV 用とテキスト用の 2 回)。残る時間の大半は図版の検出(vips・1 ページ約 3 秒)である。
|
|
54
|
+
- **OCR の言語を書かないときの既定を、日本語だけから日本語と英語(`jpn+eng`)にした**。`english` を `eng` へ読み替えるようにもした。英語を足すと、コードを含むページで識別子が 18 語中 7 → 16 語戻り、和文の文字の適合率も 93.7% → 97.6% に上がった。英字の少ない本でも和文は悪くならない(2 冊で測定)。
|
|
55
|
+
- **対応 Ruby を 3.4.10 / 4.0.9 にした**(`.ruby-version`・`rake test:versions`)。4.0.9 は 2026-10-23 公開。
|
|
56
|
+
- **依存を最新へ上げた**: HexaPDF 1.6.0 → 1.10.0、Minitest → 6.0.6、minitest-reporters 1.7.1 → 1.8.0。
|
|
57
|
+
- **rubocop を入れた**(`.rubocop.yml`・`rake rubocop`・CI)。見るのは `Lint` と `Security` だけで、引用符や行数は見ない。読みやすさを削ってまで様式に従うものではなく、本体 `vivlio-starter` も同じ構えを採っている。現状 0 件。
|
|
58
|
+
- **OCR の補正表を、校正辞書から独立した `config/ocr_corrections.yml` へ移した** (`reader.rb`): 走査の誤りと表記の方針は別物である。校正辞書に置くと**全原稿**に当たるため、本体側で実測すると `Al => AI`(OCR が AI をエルの小文字と読み違える補正)がアルミニウムの元素記号と人名 Al Gore を書き換え、`語芸 => 語彙` が「言語芸術」を「言語彙術」にしていた。しかも prh 由来の指摘は自動修正を持つので、`vs lint --fix` が黙って壊す。補正表を分ければ**当たるのは OCR したページのテキストだけ**になる。
|
|
59
|
+
- 書式は従来どおり(`expected` + `patterns`、`/…/` で正規表現)。読み取る本ごとに癖が違うので、著者が育てられるよう YAML のままにしてある。
|
|
60
|
+
- `Al => AI` は既定に入れない。`Al` が AI なのかアルミニウムなのかは文脈でしか決まらず、機械に任せる項目ではない(雛形にコメントで残し、必要な本だけ有効にできるようにした)。
|
|
61
|
+
- 補正表が無いプロジェクト(1.0 より前の雛形)では、従来どおり補正せずに素通りする。
|
|
62
|
+
- **README を実装に合わせた**: 機能表から隠しノンブルを外し、OCR 補正の出どころを `config/ocr_corrections.yml` と明記した(補正表を prh 辞書から移したのに README が追随していなかった)。
|
|
63
|
+
|
|
64
|
+
## [1.1.2] - 2026-08-07
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
- **必要な Ruby を 4.0 以上から 3.4 以上へ緩めた** (`vivlio-starter-pdf.gemspec`): Ruby 4.0 固有の機能は使っていない。Prism のバージョン指定パースで全 13 ファイルを解析したところ 4.0.0 / 3.4.0 / 3.3.0 いずれも構文エラー 0 件で、実際の下限を決めていたのは `it`(暗黙ブロック引数・38 箇所)**単独**だった。`it` は 3.3 でも構文エラーにならず実行時 `NameError` になる——静的解析では見つからないので、Ruby 3.4.10 で実走して全テスト通過を確認している。コード変更はゼロ(gemspec 1 行のみ)。本体 `vivlio-starter` の緩和に合わせたもので、本体だけ緩めてもプラグインが `>= 4.0` のままでは 3.4 環境に入らない。
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
- **壊れたアウトライン項目 1 件が、しおりを全滅させていたのを直した** (`outline_writer.rb`): 見出しのタイトルに不正な UTF-8 バイトが混ざると `String#strip` が `Encoding::CompatibilityError` を投げ、`add_outline!` の rescue まで飛んで **PDF からしおりが丸ごと消えていた**。範囲外ページ・空タイトル・深すぎる階層はいずれも `on_skip` で 1 件ずつ飛ばす設計なのに、ここだけ全体を巻き添えにしていた。
|
|
71
|
+
- `add_outline!` は `false` を返すだけで PDF 自体は出来上がるため、**成果物を開くまで誰も気付けない**種類の失敗だった。
|
|
72
|
+
- 該当項目だけを飛ばし、理由(`title is not valid UTF-8, skipping outline entry: …`)を `on_skip` で報告する。原稿のどの見出しを直せばよいか分かる形にした。
|
|
73
|
+
- `EncodingError` と `ArgumentError` の両方を捕まえる。壊れたバイト列に Ruby がどちらを投げるかはメソッド次第で(`strip` は `Encoding::CompatibilityError`、`match?` は `ArgumentError`)、その差でしおりの有無が決まってはならない。
|
|
74
|
+
- **アウトライン項目に `nil` 以外の想定外の型が混ざると全体が止まっていたのを直した** (`outline_writer.rb`): `fetch` が `respond_to?(:[])` だけで添字アクセスしていたため、配列に `String` や `Integer` が紛れると `TypeError: no implicit conversion of Symbol into Integer`(`Integer#[]` はビット参照)で落ちていた。Hash 風オブジェクトは従来どおり受けつつ、添字アクセスの失敗は既定値へ倒す。
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- **堅牢性テストを整備した** (`test/robustness/`): 本体 `vivlio-starter` の `test/vivlio_starter/robustness/` と同じ位置づけで 14 件を追加。守る約束は 2 つ——**公開 API は例外を外へ出さない**(ビルドパイプラインの途中から呼ばれるため、失敗は戻り値で伝える)、**失敗しても入力を壊さない**(読み込んだのと同じパスへ書き戻すため、途中失敗で元 PDF が切り詰められると最も気付きにくい事故になる)。上記 2 件の修正はこのテストを書く過程で見つかったもの。
|
|
78
|
+
- `broken_pdf_test.rb` — PDF でないファイル・0 バイト・存在しないパス・暗号化 PDF・書き込み不可ファイル。戻り値と、**入力のバイト列が不変であること**を固定
|
|
79
|
+
- `outline_entry_test.rb` — 壊れた項目を 1 件ずつ飛ばして残りを書くこと、飛ばした理由を報告すること、全滅時に空のしおりツリーを残さないこと
|
|
80
|
+
|
|
8
81
|
## [1.1.1] - 2026-06-23
|
|
9
82
|
|
|
10
83
|
### Fixed
|
|
@@ -69,13 +142,3 @@ vivlio-starter-pdf 1.0.0 としてリリース!HexaPDF を活用した高度
|
|
|
69
142
|
- 日本語テキストの空白圧縮処理
|
|
70
143
|
- 括弧の正規化と誤読修正
|
|
71
144
|
- 出版向けの文字処理最適化
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## [Unreleased]
|
|
76
|
-
|
|
77
|
-
### Planned
|
|
78
|
-
- パフォーマンス最適化(大規模 PDF 対応)
|
|
79
|
-
- 追加 OCR エンジン対応
|
|
80
|
-
- クラウド OCR サービス連携
|
|
81
|
-
- PDF 暗号化対応
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# vivlio-starter-pdf
|
|
2
2
|
|
|
3
3
|
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
4
|
-
[](https://www.ruby-lang.org/)
|
|
5
5
|
|
|
6
6
|
**vivlio-starter** の AGPL 拡張プラグイン。HexaPDF を活用した高度な PDF 解析・後処理機能を提供します。
|
|
7
7
|
|
|
@@ -13,28 +13,22 @@ vivlio-starter 本体(MIT)が提供する Standard Mode を拡張し、以
|
|
|
13
13
|
| --- | --- |
|
|
14
14
|
| **PDF → Markdown 変換** | HexaPDF でテキスト・画像を高精度に抽出し、Markdown に変換 |
|
|
15
15
|
| **画像抽出(WebP 化)** | PDF 内の XObject を解析し、WebP 形式で書き出し |
|
|
16
|
-
| **OCR 連携** | スキャン PDF を自動検出し、Tesseract
|
|
17
|
-
| **OCR テキスト補正** |
|
|
18
|
-
|
|
|
19
|
-
| **PDF アウトライン** | HTML 見出しを解析し、PDF のブックマークツリーを構築 |
|
|
16
|
+
| **OCR 連携** | スキャン PDF を自動検出し、Tesseract で OCR を実行(既定は日本語と英語) |
|
|
17
|
+
| **OCR テキスト補正** | 日本語空白圧縮、括弧正規化、`config/ocr_corrections.yml` による誤読修正 |
|
|
18
|
+
| **PDF アウトライン** | 本体から渡された見出しの一覧をもとに、PDF のブックマークツリーを構築 |
|
|
20
19
|
|
|
21
20
|
## インストール
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
本体の導入スクリプトで Vivlio Starter をインストールした場合は、この gem と外部ツールも同時にインストールされています。追加の操作は必要ありません。
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
gem install vivlio-starter-pdf
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
インストール完了時に、OCR 機能を利用するために必要な外部ツールも案内されます。
|
|
30
|
-
|
|
31
|
-
### 外部ツール(OCR 利用時)
|
|
24
|
+
それ以外の方法で本体をインストールした場合は、次のコマンドを実行します。
|
|
32
25
|
|
|
33
26
|
```zsh
|
|
34
|
-
|
|
27
|
+
gem install vivlio-starter-pdf
|
|
28
|
+
vs doctor --fix
|
|
35
29
|
```
|
|
36
30
|
|
|
37
|
-
`vs
|
|
31
|
+
`vs doctor --fix` が、OCR と画像抽出に必要な外部ツール([一覧](#外部ツール))をインストールします。
|
|
38
32
|
|
|
39
33
|
## 使い方
|
|
40
34
|
|
|
@@ -46,40 +40,53 @@ brew install tesseract tesseract-lang poppler vips
|
|
|
46
40
|
vs pdf:read document.pdf
|
|
47
41
|
```
|
|
48
42
|
|
|
43
|
+
本体はこの gem を require せず、実行ファイル `vivlio-starter-pdf read PDF key=value …` を別プロセスとして呼び、標準出力の JSON を受け取ります。AGPL のコードを MIT の本体へ取り込まないための分け方です。
|
|
44
|
+
|
|
45
|
+
標準(MIT)実装に戻して動作を比べたいときは、環境変数で無効化できます。
|
|
46
|
+
|
|
47
|
+
```zsh
|
|
48
|
+
VIVLIO_PDF_PLUGIN=disable vs build
|
|
49
|
+
```
|
|
50
|
+
|
|
49
51
|
### Ruby API
|
|
50
52
|
|
|
51
53
|
```ruby
|
|
52
|
-
require "
|
|
54
|
+
require "vivlio_starter/pdf"
|
|
53
55
|
|
|
54
56
|
# PDF → Markdown 変換
|
|
55
|
-
result =
|
|
56
|
-
ocr: { mode: "auto", languages: ["jpn"], dpi: 300 }
|
|
57
|
+
result = VivlioStarter::Pdf::Reader.new("input.pdf",
|
|
58
|
+
ocr: { mode: "auto", languages: ["jpn", "eng"], dpi: 300 }
|
|
57
59
|
).execute
|
|
58
60
|
|
|
59
61
|
# アウトライン付与
|
|
60
|
-
provider =
|
|
62
|
+
provider = VivlioStarter::Pdf::EnhancedProvider.new
|
|
61
63
|
provider.add_outline!(pdf_path, items, max_level: 3)
|
|
62
64
|
|
|
63
|
-
#
|
|
64
|
-
provider.
|
|
65
|
+
# ページ数
|
|
66
|
+
provider.page_count(pdf_path)
|
|
65
67
|
```
|
|
66
68
|
|
|
69
|
+
`add_outline!` は成功で `true`、失敗で `false` を返し、`page_count` は失敗で `nil` を返します。**例外は外へ出しません**(本体のビルドパイプラインの途中から呼ばれるため)。失敗しても入力 PDF は書き換えません。
|
|
70
|
+
|
|
71
|
+
`add_outline!` の `items` は `{ level:, text:, page: }` の配列です。`top:`(見出しの y 座標)を加えると、しおりの移動先がその位置になります。省くとページ全体が移動先になります。壊れた項目(不正な UTF-8 のタイトル・範囲外ページ・空タイトル)は 1 件ずつ飛ばして残りを書き込み、飛ばした理由をログに出します。
|
|
72
|
+
|
|
67
73
|
## 設定
|
|
68
74
|
|
|
69
75
|
`config/book.yml` の `pdf_read` セクションで挙動を制御できます。
|
|
70
76
|
|
|
71
77
|
```yaml
|
|
72
78
|
pdf_read:
|
|
73
|
-
text_area:
|
|
74
|
-
top_margin:
|
|
75
|
-
bottom_margin:
|
|
76
|
-
inner_margin:
|
|
77
|
-
outer_margin:
|
|
79
|
+
text_area: # 柱が本文に混ざるときだけ、除く幅を mm で書く
|
|
80
|
+
top_margin:
|
|
81
|
+
bottom_margin:
|
|
82
|
+
inner_margin:
|
|
83
|
+
outer_margin:
|
|
78
84
|
page_separator: false
|
|
79
85
|
ocr:
|
|
80
86
|
mode: auto
|
|
81
87
|
languages:
|
|
82
88
|
- japanese
|
|
89
|
+
- english
|
|
83
90
|
dpi: 300
|
|
84
91
|
psm: 3
|
|
85
92
|
inline_image_text: include
|
|
@@ -92,13 +99,14 @@ pdf_read:
|
|
|
92
99
|
| [HexaPDF](https://hexapdf.gettalong.org/) | ~> 1.0 | PDF 解析・編集 |
|
|
93
100
|
| [ruby-vips](https://github.com/libvips/ruby-vips) | ~> 2.2 | 高速画像処理 |
|
|
94
101
|
|
|
95
|
-
###
|
|
102
|
+
### 外部ツール
|
|
96
103
|
|
|
97
104
|
| ツール | 用途 |
|
|
98
105
|
| --- | --- |
|
|
99
|
-
| Tesseract | OCR
|
|
100
|
-
| poppler(pdftoppm) | PDF →
|
|
106
|
+
| Tesseract、tesseract-lang | OCR エンジンと言語データ |
|
|
107
|
+
| poppler(pdftoppm、pdfimages) | PDF → 画像変換、HexaPDF が書き出せない画像の抽出 |
|
|
101
108
|
| libvips | 画像処理バックエンド |
|
|
109
|
+
| ImageMagick | 抽出した画像の WebP 変換 |
|
|
102
110
|
|
|
103
111
|
## 開発
|
|
104
112
|
|
|
@@ -106,7 +114,13 @@ pdf_read:
|
|
|
106
114
|
git clone https://github.com/Atelier-Mirai/vivlio-starter-pdf.git
|
|
107
115
|
cd vivlio-starter-pdf
|
|
108
116
|
bundle install
|
|
109
|
-
bundle exec rake
|
|
117
|
+
bundle exec rake # rubocop(Lint / Security)+ 対応する全 Ruby でテスト
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
統合テストが読む PDF は `test/fixtures/` に同梱しています。中身を変えたいときだけ作り直してください(HackGen35 = SIL OFL-1.1 を埋め込みます)。
|
|
121
|
+
|
|
122
|
+
```zsh
|
|
123
|
+
bundle exec ruby test/fixtures/build_fixtures.rb
|
|
110
124
|
```
|
|
111
125
|
|
|
112
126
|
## ライセンス
|
data/RELEASE_NOTE.md
CHANGED
|
@@ -1,133 +1,92 @@
|
|
|
1
|
-
# vivlio-starter-pdf
|
|
1
|
+
# vivlio-starter-pdf リリースノート
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
書籍制作ツール **vivlio-starter** の AGPL 拡張プラグインです。HexaPDF を使い、PDF アウトライン(しおり)と PDF → Markdown 変換(OCR 連携)を担当します。使い方は [README](README.md)、網羅的な変更履歴は [CHANGELOG](CHANGELOG.md) をご覧ください。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
新しい版から順に並べています。
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 1.2.0 — 2026-11-01
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- HexaPDF でテキスト・画像を高精度に抽出
|
|
11
|
-
- 精密な座標解析による行・段落の再構成
|
|
12
|
-
- Markdown 形式での構造化出力
|
|
9
|
+
**スキャンした PDF の読み取りを、速く、正確にしました。**
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- Tesseract
|
|
17
|
-
- OCR
|
|
18
|
-
-
|
|
11
|
+
- **OCR が約 4 倍速くなりました。** ページ全体を覆う画像があれば、ページを描き直さず、その画像を Tesseract へ渡します。24 ページのスキャンで 624 秒が 151 秒になり、読み取りの結果は変わりません
|
|
12
|
+
- **既定の言語を日本語と英語(`jpn+eng`)にしました。** コードを含むページで、18 語の識別子のうち正しく読めた語が 7 語から 16 語に増えました。英字の語が 20 以上あるページは英語だけの設定でも OCR し、英字の語は確信度の高いほうの結果を採用します
|
|
13
|
+
- **表、色の帯の中の見出し、斜体の行も読めるようになりました。** Tesseract が文字を検出しなかった範囲と斜体の行を切り出して OCR し直し、確信度の高い字がもとの結果より多いときだけ置き換えます
|
|
14
|
+
- **傾いたスキャンを補正し、解像度が 400dpi を超える画像は 300dpi に縮小してから OCR します。** 地の色の付いた囲みが多いページは二値化した画像でも OCR し、確信度の高い字が多いほうの結果を採用します
|
|
15
|
+
- **図版の検出を作り直しました。** 図 15 枚のうち 1 枚しか検出できなかったスキャンで、14 枚を誤検出なしで検出するようになりました
|
|
16
|
+
- **読み取った語の位置とページの大きさを結果に含めました。** 本体の `vs pdf:read` はこの情報から、見出し・段落・コードを推定します
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
- PDF 内の XObject を解析し、WebP 形式で書き出し
|
|
22
|
-
- テキスト行と画像の精密な座標マッピング
|
|
23
|
-
- イラスト領域の自動検出と除外
|
|
18
|
+
**便利になったこと**
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
20
|
+
- **しおりから見出しの位置へ移動できるようになりました。** 本体が渡す節と項の y 座標(`:top`)を移動先に設定します。左端と倍率は変更しません
|
|
21
|
+
- **`vs pdf:read --pages` で、読むページを指定できるようになりました。** 大きな本の一部を読むときの処理時間を短縮できます
|
|
22
|
+
- **`vs pdf:read --log` で、OCR の進行状況を表示するようになりました**
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
**隠しノンブルの機能を削除しました。** 本体 vivlio-starter が Prawn と qpdf で描画するようになり、**このプラグインのノンブル機能はどこからも呼ばれていませんでした**。ノンブルの見た目や位置はこれまでどおりで、`vs build` の成果物は変わりません。この gem の機能は、**しおりの付与**と **PDF → Markdown 変換**の 2 つになりました。
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
# Gemfile
|
|
33
|
-
gem 'vivlio-starter-pdf', '~> 1.0.0'
|
|
34
|
-
```
|
|
26
|
+
インストール後のメッセージ(`post_install_message`)を削除しました。OCR に必要な外部ツールは、本体の `vs doctor --fix` がインストールします。
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
bundle install
|
|
38
|
-
```
|
|
28
|
+
あわせて、テストと開発環境を整えました。
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
- **PDF を読み通す統合テスト 2 件が、skip されたままになっていました。** 読み込む PDF が git で管理されていない手元のファイルだったため、CI では実行されず、開発機からファイルが無くなった後は開発機でも実行されていませんでした。ファイルが無いときはテストが skip されるので、失敗としては報告されません。テスト用の小さな PDF を作ってリポジトリの `test/fixtures/` に置き、常に実行されるようにしました(gem には含まれません)
|
|
31
|
+
- **OCR 後の字間の除去にテストを追加しました。** スキャンを OCR した PDF は「日 本 語 を 自 然 に」のように字の間に空白が入ります。この空白を除去する処理には、テストがありませんでした
|
|
32
|
+
- **対応 Ruby を 3.4.10 / 4.0.9 に更新しました**(4.0.9 は 2026 年 10 月 23 日公開)
|
|
33
|
+
- **HexaPDF を 1.10.0 へ、Minitest を 6 系へ更新しました**
|
|
34
|
+
- **コード検査(rubocop)を導入しました。** 有効にしているのは `Lint` と `Security` の規則だけで、引用符の種類などの様式は検査しません
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
brew install tesseract tesseract-lang poppler vips
|
|
44
|
-
```
|
|
36
|
+
### 不具合の修正
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
- **`vs pdf:read` の Enhanced Mode が、この版で初めて動作します。** 本体は Enhanced Mode でこの gem の実行ファイルを `read` 引数付きで起動しますが、実行ファイルは `--version` と `--help` しか受け付けず、`Unknown option 'read'` で終了していました。本体のテストは呼び出しをスタブに置き換えていたため、この失敗を検出できませんでした。`read` サブコマンドを追加し、実行ファイルを子プロセスとして起動するテストを追加しました。スキャンした PDF を読むには Enhanced Mode が必要です。以前読めなかった PDF は、更新後にもう一度お試しください
|
|
39
|
+
- **OCR の誤読修正が、2026 年 8 月 4 日から適用されていませんでした。** 修正表の読み込み先が、本体側で改名される前のファイル名のままでした。ファイルが無いと**空の修正表が使われ**、エラーも警告も出ませんでした
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
あわせて、修正表を校正辞書から `config/ocr_corrections.yml` へ分離しました。校正辞書に置くと**原稿全体**に適用され、`Al → AI`(OCR が AI の I を小文字のエルと誤認識したときの修正)がアルミニウムの元素記号や人名を書き換え、`語芸 → 語彙` が「言語芸術」を「言語彙術」に変えていました。分離後は、OCR したページだけに適用されます。誤認識の傾向は本によって異なるので、修正表は YAML のままです。お手元の本に合わせて項目を追加してください
|
|
42
|
+
- **各行の文字列が重複していたのを直しました。** 文字を 1 字ずつ描画する PDF で、行が「全文+先頭の 1 字を欠いた全文」になっていました。Vivliostyle が生成する PDF は 1 字ずつ描画するので、`vs build` の PDF では全行が該当しました
|
|
43
|
+
- **OCR を無効にする値 `disable` が効くようになりました。** この値を認識せず `auto` として扱っていました。さらに、HexaPDF が例外を出すページの文字を破棄していたため、文字の層のある PDF でも全ページを OCR し、300 ページ台の本で 15 分を超えても終了しませんでした。修正後は同じ本を 10 秒以内に読めます
|
|
44
|
+
- **HexaPDF が書き出せない画像を、`pdfimages` で書き出します。** 103 個のうち 9 個しか抽出できなかった PDF で、103 個すべてを抽出できるようになりました
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
# Enhanced Mode で自動的に HexaPDF/OCR パイプラインを使用
|
|
52
|
-
vs pdf:read document.pdf
|
|
53
|
-
```
|
|
46
|
+
## 1.1.2 — 2026-08-07
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
**細かな不具合修正が中心のリリースです。** ただし 1 件は成果物に直接影響するため、しおり付き PDF を作る方は更新をおすすめします。
|
|
56
49
|
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
- **壊れた見出しが 1 つあると、しおりが丸ごと消えていた問題を直しました。** 見出しに不正な UTF-8 バイトが混ざると例外が最上位まで飛び、**PDF からしおりが全滅**していました。範囲外ページや空タイトルは 1 件ずつ飛ばす設計だったのに、ここだけ全体を巻き添えにしていたことになります。しかも PDF 自体は出来上がるため、**開いてみるまで誰も気付けません**。今は該当項目だけを飛ばし、どの見出しが原因かをログで知らせます
|
|
51
|
+
- **想定外の型が項目に混ざると、アウトライン付与が止まっていた問題を直しました**(`TypeError`)
|
|
52
|
+
- **必要な Ruby を 4.0 以上から 3.4 以上へ緩めました**(コード変更はゼロ、gemspec 1 行のみ)。本体の緩和に合わせたものです。本体だけ緩めても、プラグインが 4.0 以上のままでは 3.4 環境に入りません
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
result = Vivlio::Starter::PDF::Reader.new("input.pdf",
|
|
62
|
-
ocr: { mode: "auto", languages: ["jpn"], dpi: 300 }
|
|
63
|
-
).execute
|
|
54
|
+
あわせて、これらを二度と起こさないための**堅牢性テスト 14 件**を `test/robustness/` に追加しました。守る約束は 2 つです。
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
provider.add_outline!(pdf_path, items, max_level: 3)
|
|
56
|
+
1. **公開 API は例外を外へ出さない。** 本体のビルドパイプラインの途中から呼ばれるため、1 つの PDF 操作が例外で飛ぶとビルド全体が落ちます。失敗は戻り値で伝えます
|
|
57
|
+
2. **失敗しても入力を壊さない。** 読み込んだのと同じパスへ書き戻すので、途中で失敗して元 PDF が切り詰められると「ビルドは続いたのに成果物が壊れている」という最も気付きにくい事故になります
|
|
68
58
|
|
|
69
|
-
|
|
70
|
-
provider.stamp_nombre!(pdf_path, bleed_pt: 8.5)
|
|
71
|
-
```
|
|
59
|
+
上の 2 件は、このテストを書く過程で見つかりました。
|
|
72
60
|
|
|
73
|
-
##
|
|
61
|
+
## 1.1.1 — 2026-06-23
|
|
74
62
|
|
|
75
|
-
|
|
63
|
+
- **隠しノンブルを埋め込み可能なフォントで描画するようにしました(FT-02)。** HexaPDF 組込の標準 14 フォント `Helvetica` は PDF に埋め込まれず、印刷所入稿で「非埋め込みフォント」の差し戻しになっていました
|
|
64
|
+
- 書籍プロジェクト直下に同梱の `stylesheets/fonts/hackgen35/HackGen35ConsoleNF-Regular.ttf` があれば、数字グリフのみをサブセット埋め込みします(増分は極小)。無ければ従来どおり `Helvetica` にフォールバックしてビルドを続けます
|
|
76
65
|
|
|
77
|
-
|
|
78
|
-
pdf_read:
|
|
79
|
-
text_area:
|
|
80
|
-
top_margin: 18
|
|
81
|
-
bottom_margin: 20
|
|
82
|
-
inner_margin: 15
|
|
83
|
-
outer_margin: 12
|
|
84
|
-
page_separator: false
|
|
85
|
-
ocr:
|
|
86
|
-
mode: auto
|
|
87
|
-
languages:
|
|
88
|
-
- japanese
|
|
89
|
-
dpi: 300
|
|
90
|
-
psm: 3
|
|
91
|
-
inline_image_text: include
|
|
92
|
-
```
|
|
66
|
+
## 1.1.0 — 2026-06-10
|
|
93
67
|
|
|
94
|
-
|
|
68
|
+
- **名前空間を `VivlioStarter::Pdf` へ統一し、`lib/vivlio_starter/` 配下へ再編しました。** 1.0.0 以降で**唯一の非互換変更**です
|
|
95
69
|
|
|
96
|
-
|
|
97
|
-
- **実環境での動作検証**
|
|
98
|
-
- **外部ツール連携のテスト**
|
|
70
|
+
本体(`vs`)経由で使っている場合、影響はありません。**Ruby API を直接呼んでいる場合のみ**書き換えが必要です。
|
|
99
71
|
|
|
100
|
-
|
|
72
|
+
| | 1.0.x | 1.1.0 以降 |
|
|
73
|
+
|:---|:---|:---|
|
|
74
|
+
| require | `require "vivlio/starter/pdf"` | `require "vivlio_starter/pdf"` |
|
|
75
|
+
| 名前空間 | `Vivlio::Starter::PDF` と `Vivlio::Starter::Pdf` が混在 | `VivlioStarter::Pdf` に統一 |
|
|
101
76
|
|
|
102
|
-
|
|
103
|
-
- 完全な OCR 連携と日本語対応
|
|
104
|
-
- 画像位置合わせの精密化
|
|
105
|
-
- PDF アウトライン生成機能
|
|
106
|
-
- 隠しノンブル機能
|
|
77
|
+
1.0.x では `PDF` と `Pdf` の 2 つの綴りが同居しており、どちらを書けばよいか実物を見ないと分からない状態でした。
|
|
107
78
|
|
|
108
|
-
|
|
109
|
-
- gemspec の RubyGems 公開設定
|
|
110
|
-
- ドキュメントの整備
|
|
111
|
-
- 外部ツールの自動案内
|
|
79
|
+
## 1.0.1 — 2026-03-21
|
|
112
80
|
|
|
113
|
-
|
|
81
|
+
- **CLI を追加しました**(`vivlio-starter-pdf --version` / `--help`)。インストール直後に動作確認できます
|
|
114
82
|
|
|
115
|
-
|
|
116
|
-
- **日本語出版** 向けの最適化完了
|
|
117
|
-
- **HexaPDF** との高度な連携実現
|
|
118
|
-
- **AGPL-3.0** ライセンス準拠
|
|
83
|
+
## 1.0.0 — 2026-03-21
|
|
119
84
|
|
|
120
|
-
|
|
85
|
+
最初のメジャーリリースです。
|
|
121
86
|
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
vivlio-starter-pdf の開発にあたり、HexaPDF の強力な PDF 処理能力と、日本語出版の現場でのニーズが大きな助けとなりました。特に OCR 連携と日本語テキスト処理についての知見は、本プラグインをより実用的なものにする上で不可欠でした。
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
**vivlio-starter-pdf 1.0.0**: 高度な PDF 処理のための強力な拡張プラグイン
|
|
87
|
+
- **隠しノンブル** — 入稿用 PDF の塗り足し領域に、断裁されて消えるページ番号を刷り込みます
|
|
88
|
+
- **PDF アウトライン** — 見出しから PDF のブックマークツリーを構築します
|
|
89
|
+
- **PDF → Markdown 変換** — HexaPDF でテキスト・画像を座標付きで抽出します
|
|
90
|
+
- **OCR 連携** — スキャン PDF を自動検出し、Tesseract で日本語 OCR を実行。空白圧縮・括弧正規化・prh 辞書による誤読修正まで行います
|
|
91
|
+
- **画像抽出** — PDF 内の XObject を解析し、WebP 形式で書き出します
|
|
92
|
+
- プラグイン専用設計。インストールされていれば本体が自動検出し、Standard Mode から Enhanced Mode へ切り替わります
|
data/exe/vivlio-starter-pdf
CHANGED
|
@@ -1,48 +1,106 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'json'
|
|
4
5
|
require 'vivlio_starter/pdf'
|
|
5
6
|
|
|
6
7
|
# vivlio-starter-pdf CLI
|
|
7
|
-
#
|
|
8
|
-
|
|
8
|
+
#
|
|
9
|
+
# 本体 vivlio-starter(MIT)は AGPL のこの gem を require せず、別プロセスとして呼ぶ。
|
|
10
|
+
# `read` はその窓口で、`vs pdf:read` の Enhanced Mode が使う。人が直接打つ想定ではない。
|
|
9
11
|
module VivlioStarter
|
|
10
12
|
module Pdf
|
|
11
13
|
class CLI
|
|
14
|
+
# `read` が受け付ける設定。値は `key=value` で渡し、Hash は JSON で書く。
|
|
15
|
+
# 本体の PdfReadCommand#enhanced_command が組み立てる引数と対になっている
|
|
16
|
+
READ_OPTION_PARSERS = {
|
|
17
|
+
'page_separator' => ->(value) { value == 'true' },
|
|
18
|
+
'text_area' => ->(value) { JSON.parse(value) },
|
|
19
|
+
'line_merge_tolerance' => ->(value) { Float(value) },
|
|
20
|
+
'images_dir' => ->(value) { value },
|
|
21
|
+
'image_reference_dir' => ->(value) { value },
|
|
22
|
+
'ocr' => ->(value) { JSON.parse(value) },
|
|
23
|
+
'progress' => ->(value) { value == 'true' },
|
|
24
|
+
'pages' => ->(value) { JSON.parse(value) }
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
12
27
|
def self.run(args = [])
|
|
13
|
-
case args
|
|
14
|
-
|
|
28
|
+
case args
|
|
29
|
+
in ['--version' | '-v', *]
|
|
15
30
|
puts "vivlio-starter-pdf #{VERSION}"
|
|
16
|
-
|
|
17
|
-
puts
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Options:
|
|
24
|
-
--version, -v Print version and exit
|
|
25
|
-
--help, -h Print this help and exit
|
|
26
|
-
|
|
27
|
-
Description:
|
|
28
|
-
vivlio-starter-pdf is an AGPL-licensed plugin for vivlio-starter
|
|
29
|
-
that provides advanced PDF processing capabilities including:
|
|
30
|
-
- PDF to Markdown conversion using HexaPDF
|
|
31
|
-
- Japanese OCR support with Tesseract
|
|
32
|
-
- Image extraction and precise positioning
|
|
33
|
-
- PDF outline generation and hidden pagination
|
|
34
|
-
|
|
35
|
-
This is a plugin and should be used through vivlio-starter:
|
|
36
|
-
vs pdf:read document.pdf
|
|
37
|
-
|
|
38
|
-
For more information, visit: https://github.com/Atelier-Mirai/vivlio-starter-pdf
|
|
39
|
-
HELP
|
|
31
|
+
in ['--help' | '-h', *] | []
|
|
32
|
+
puts help_text
|
|
33
|
+
in ['read', pdf_path, *settings]
|
|
34
|
+
read(pdf_path, settings)
|
|
35
|
+
in ['read']
|
|
36
|
+
fail_with('read には PDF のパスを指定してください')
|
|
40
37
|
else
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
fail_with("Unknown option '#{args[0]}'\nUse 'vivlio-starter-pdf --help' for usage information.")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# PDF を読み、Reader#execute の結果を JSON で標準出力へ書く。
|
|
43
|
+
# 標準出力は JSON だけにする。呼び出し側は標準出力をそのまま JSON.parse するので、
|
|
44
|
+
# 警告や進捗は標準エラーへ出すこと
|
|
45
|
+
def self.read(pdf_path, settings)
|
|
46
|
+
fail_with("PDF が見つかりません: #{pdf_path}") unless File.file?(pdf_path)
|
|
47
|
+
|
|
48
|
+
options = parse_read_options(settings)
|
|
49
|
+
# 経過は標準エラーへ「progress: ocr 5/24」の形で書く。本体が --log のときに中継する
|
|
50
|
+
options[:progress] = options[:progress] ? ->(page, total) { $stderr.puts("progress: ocr #{page}/#{total}") || $stderr.flush } : nil
|
|
51
|
+
result = Reader.new(pdf_path, **options.compact).execute
|
|
52
|
+
$stdout.write(JSON.generate(result))
|
|
53
|
+
rescue JSON::ParserError, ArgumentError => e
|
|
54
|
+
fail_with("read の設定を解釈できません: #{e.message}")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# `key=value` の並びを Reader.new のキーワード引数へ変える。未知のキーは誤りとして終了する
|
|
58
|
+
# (本体と版が異なるときに、設定が無視されるのを防ぐ)
|
|
59
|
+
def self.parse_read_options(settings)
|
|
60
|
+
settings.to_h do |setting|
|
|
61
|
+
key, value = setting.split('=', 2)
|
|
62
|
+
parser = READ_OPTION_PARSERS[key]
|
|
63
|
+
raise ArgumentError, "知らない設定です: #{key}" if parser.nil? || value.nil?
|
|
64
|
+
|
|
65
|
+
[key.to_sym, parser.(value)]
|
|
44
66
|
end
|
|
45
67
|
end
|
|
68
|
+
|
|
69
|
+
def self.fail_with(message)
|
|
70
|
+
warn "Error: #{message}"
|
|
71
|
+
exit 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.help_text
|
|
75
|
+
<<~HELP
|
|
76
|
+
vivlio-starter-pdf #{VERSION} - Advanced PDF processor for vivlio-starter
|
|
77
|
+
|
|
78
|
+
Usage:
|
|
79
|
+
vivlio-starter-pdf [options]
|
|
80
|
+
vivlio-starter-pdf read PDF [key=value ...]
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
--version, -v Print version and exit
|
|
84
|
+
--help, -h Print this help and exit
|
|
85
|
+
|
|
86
|
+
Commands:
|
|
87
|
+
read Read PDF and print the result as JSON (used by `vs pdf:read`)
|
|
88
|
+
keys: #{READ_OPTION_PARSERS.keys.join(', ')}
|
|
89
|
+
|
|
90
|
+
Description:
|
|
91
|
+
vivlio-starter-pdf is an AGPL-licensed plugin for vivlio-starter
|
|
92
|
+
that provides advanced PDF processing capabilities including:
|
|
93
|
+
- PDF to Markdown conversion using HexaPDF
|
|
94
|
+
- Japanese OCR support with Tesseract
|
|
95
|
+
- Image extraction and precise positioning
|
|
96
|
+
- PDF outline generation
|
|
97
|
+
|
|
98
|
+
This is a plugin and should be used through vivlio-starter:
|
|
99
|
+
vs pdf:read document.pdf
|
|
100
|
+
|
|
101
|
+
For more information, visit: https://github.com/Atelier-Mirai/vivlio-starter-pdf
|
|
102
|
+
HELP
|
|
103
|
+
end
|
|
46
104
|
end
|
|
47
105
|
end
|
|
48
106
|
end
|