vivlio-starter-pdf 1.1.1 → 1.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f467238756b956810a17babdb5808c1d1ee08596fb4cf5a2d4437ee19ac1bdd9
4
- data.tar.gz: cc1b31492ec07fdb0aa2fce8bdfe5d84c9e9ffe89584c3c54c39c437233e1d5b
3
+ metadata.gz: 0335b9ec25ee9a373f9d4c261c1a4a610e87fb2ac538b2207b3dd8f741bc82c1
4
+ data.tar.gz: 9645fdbcb4575cae982bf48ef0c60f061b6f26afbbe101bd4530f95262c6da32
5
5
  SHA512:
6
- metadata.gz: 8593d07317b5ec3eefd0b002441e77884eca92ef38271ea91f2ad19b5f33404f7fd750c60641112a274b35a2eb2bd6d183a10cac97e6630afa8bce38d81b7e68
7
- data.tar.gz: 9cd7704acb4d156e639d68989b5229aa8ef497f89a3fc1ea28e04b4ce3a9a1ca7580cd0e0d263c7658b656da4e0a1eb08537534c9e3ec1b294fff9bd9dd29236
6
+ metadata.gz: 01f2c370439f11cf3012f32b092415a8aeebab059386f09462292952e7026b8b616db82e00aae11489465d562c0f90f89ea548f6df98d89523e05c7b0f1e8a00
7
+ data.tar.gz: 215ff34974f8bc3f2fd81f929bd39413ff79ad454db5993ee36c350dd48af12b091372b9b1ca42c6b048e29d5633e88b29bfb5dca3aebcaa8726d4d594773f4e
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0.6
data/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ 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
+ ### Planned
9
+ - パフォーマンス最適化(大規模 PDF 対応)
10
+ - 追加 OCR エンジン対応
11
+ - クラウド OCR サービス連携
12
+ - PDF 暗号化対応
13
+
14
+ ## [1.1.2] - 2026-08-07
15
+
16
+ ### Changed
17
+ - **必要な 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 環境に入らない。
18
+
19
+ ### Fixed
20
+ - **壊れたアウトライン項目 1 件が、しおりを全滅させていたのを直した** (`outline_writer.rb`): 見出しのタイトルに不正な UTF-8 バイトが混ざると `String#strip` が `Encoding::CompatibilityError` を投げ、`add_outline!` の rescue まで飛んで **PDF からしおりが丸ごと消えていた**。範囲外ページ・空タイトル・深すぎる階層はいずれも `on_skip` で 1 件ずつ飛ばす設計なのに、ここだけ全体を巻き添えにしていた。
21
+ - `add_outline!` は `false` を返すだけで PDF 自体は出来上がるため、**成果物を開くまで誰も気付けない**種類の失敗だった。
22
+ - 該当項目だけを飛ばし、理由(`title is not valid UTF-8, skipping outline entry: …`)を `on_skip` で報告する。原稿のどの見出しを直せばよいか分かる形にした。
23
+ - `EncodingError` と `ArgumentError` の両方を捕まえる。壊れたバイト列に Ruby がどちらを投げるかはメソッド次第で(`strip` は `Encoding::CompatibilityError`、`match?` は `ArgumentError`)、その差でしおりの有無が決まってはならない。
24
+ - **アウトライン項目に `nil` 以外の想定外の型が混ざると全体が止まっていたのを直した** (`outline_writer.rb`): `fetch` が `respond_to?(:[])` だけで添字アクセスしていたため、配列に `String` や `Integer` が紛れると `TypeError: no implicit conversion of Symbol into Integer`(`Integer#[]` はビット参照)で落ちていた。Hash 風オブジェクトは従来どおり受けつつ、添字アクセスの失敗は既定値へ倒す。
25
+
26
+ ### Added
27
+ - **堅牢性テストを整備した** (`test/robustness/`): 本体 `vivlio-starter` の `test/vivlio_starter/robustness/` と同じ位置づけで 14 件を追加。守る約束は 2 つ——**公開 API は例外を外へ出さない**(ビルドパイプラインの途中から呼ばれるため、失敗は戻り値で伝える)、**失敗しても入力を壊さない**(読み込んだのと同じパスへ書き戻すため、途中失敗で元 PDF が切り詰められると最も気付きにくい事故になる)。上記 2 件の修正はこのテストを書く過程で見つかったもの。
28
+ - `broken_pdf_test.rb` — PDF でないファイル・0 バイト・存在しないパス・暗号化 PDF・書き込み不可ファイル。戻り値と、**入力のバイト列が不変であること**を固定
29
+ - `outline_entry_test.rb` — 壊れた項目を 1 件ずつ飛ばして残りを書くこと、飛ばした理由を報告すること、全滅時に空のしおりツリーを残さないこと
30
+
8
31
  ## [1.1.1] - 2026-06-23
9
32
 
10
33
  ### Fixed
@@ -69,13 +92,3 @@ vivlio-starter-pdf 1.0.0 としてリリース!HexaPDF を活用した高度
69
92
  - 日本語テキストの空白圧縮処理
70
93
  - 括弧の正規化と誤読修正
71
94
  - 出版向けの文字処理最適化
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
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
4
- [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%204.0-red.svg)](https://www.ruby-lang.org/)
4
+ [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%203.4-red.svg)](https://www.ruby-lang.org/)
5
5
 
6
6
  **vivlio-starter** の AGPL 拡張プラグイン。HexaPDF を活用した高度な PDF 解析・後処理機能を提供します。
7
7
 
@@ -46,24 +46,34 @@ brew install tesseract tesseract-lang poppler vips
46
46
  vs pdf:read document.pdf
47
47
  ```
48
48
 
49
+ 標準(MIT)実装に戻して動作を比べたいときは、環境変数で無効化できます。
50
+
51
+ ```zsh
52
+ VIVLIO_PDF_PLUGIN=disable vs build
53
+ ```
54
+
49
55
  ### Ruby API
50
56
 
51
57
  ```ruby
52
- require "vivlio/starter/pdf"
58
+ require "vivlio_starter/pdf"
53
59
 
54
60
  # PDF → Markdown 変換
55
- result = Vivlio::Starter::PDF::Reader.new("input.pdf",
61
+ result = VivlioStarter::Pdf::Reader.new("input.pdf",
56
62
  ocr: { mode: "auto", languages: ["jpn"], dpi: 300 }
57
63
  ).execute
58
64
 
59
65
  # アウトライン付与
60
- provider = Vivlio::Starter::Pdf::EnhancedProvider.new
66
+ provider = VivlioStarter::Pdf::EnhancedProvider.new
61
67
  provider.add_outline!(pdf_path, items, max_level: 3)
62
68
 
63
69
  # 隠しノンブル
64
70
  provider.stamp_nombre!(pdf_path, bleed_pt: 8.5)
65
71
  ```
66
72
 
73
+ `stamp_nombre!` / `add_outline!` は成功で `true`、失敗で `false` を返し、`page_count` は失敗で `nil` を返します。**例外は外へ出しません**(本体のビルドパイプラインの途中から呼ばれるため)。失敗しても入力 PDF は書き換えません。
74
+
75
+ `add_outline!` の `items` は `{ level:, text:, page: }` の配列です。壊れた項目(不正な UTF-8 のタイトル・範囲外ページ・空タイトル)は 1 件ずつ飛ばして残りを書き込み、飛ばした理由をログに出します。
76
+
67
77
  ## 設定
68
78
 
69
79
  `config/book.yml` の `pdf_read` セクションで挙動を制御できます。
data/RELEASE_NOTE.md CHANGED
@@ -1,133 +1,55 @@
1
- # vivlio-starter-pdf 1.0.0 Release Note
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
- vivlio-starter-pdf 1.0.0 をリリースできることを大変嬉しく思います。これは **vivlio-starter** の AGPL 拡張プラグインで、HexaPDF を活用した高度な PDF 解析・後処理機能を提供します。
5
+ 新しい版から順に並べています。
6
6
 
7
- ## 🚀 主要機能
8
-
9
- ### PDF → Markdown 変換
10
- - HexaPDF でテキスト・画像を高精度に抽出
11
- - 精密な座標解析による行・段落の再構成
12
- - Markdown 形式での構造化出力
13
-
14
- ### OCR 連携と日本語対応 🆕
15
- - スキャン PDF を自動検出
16
- - Tesseract による日本語 OCR 実行
17
- - OCR テキストの空白圧縮と括弧正規化
18
- - prh 辞書による誤読修正
19
-
20
- ### 画像抽出と位置合わせ
21
- - PDF 内の XObject を解析し、WebP 形式で書き出し
22
- - テキスト行と画像の精密な座標マッピング
23
- - イラスト領域の自動検出と除外
24
-
25
- ### 出版向け機能
26
- - **隠しノンブル**: 入稿用 PDF の塗り足し領域にページ番号をオーバーレイ
27
- - **PDF アウトライン**: HTML 見出しを解析し、PDF のブックマークツリーを構築
28
-
29
- ## 📦 インストール
30
-
31
- ```ruby
32
- # Gemfile
33
- gem 'vivlio-starter-pdf', '~> 1.0.0'
34
- ```
35
-
36
- ```bash
37
- bundle install
38
- ```
39
-
40
- ### 外部ツール(OCR 利用時)
41
-
42
- ```bash
43
- brew install tesseract tesseract-lang poppler vips
44
- ```
45
-
46
- ## 💡 使用例
47
-
48
- ### vivlio-starter との連携(プラグイン専用)
49
-
50
- ```bash
51
- # Enhanced Mode で自動的に HexaPDF/OCR パイプラインを使用
52
- vs pdf:read document.pdf
53
- ```
54
-
55
- ### Ruby API
56
-
57
- ```ruby
58
- require "vivlio/starter/pdf"
59
-
60
- # PDF → Markdown 変換
61
- result = Vivlio::Starter::PDF::Reader.new("input.pdf",
62
- ocr: { mode: "auto", languages: ["jpn"], dpi: 300 }
63
- ).execute
7
+ ---
64
8
 
65
- # アウトライン付与
66
- provider = Vivlio::Starter::Pdf::EnhancedProvider.new
67
- provider.add_outline!(pdf_path, items, max_level: 3)
9
+ ## 1.1.2 — 2026-08-07
68
10
 
69
- # 隠しノンブル
70
- provider.stamp_nombre!(pdf_path, bleed_pt: 8.5)
71
- ```
11
+ **細かな不具合修正が中心のリリースです。** ただし 1 件は成果物に直接影響するため、しおり付き PDF を作る方は更新をおすすめします。
72
12
 
73
- ## 🔧 設定
13
+ - **壊れた見出しが 1 つあると、しおりが丸ごと消えていた問題を直しました。** 見出しに不正な UTF-8 バイトが混ざると例外が最上位まで飛び、**PDF からしおりが全滅**していました。範囲外ページや空タイトルは 1 件ずつ飛ばす設計だったのに、ここだけ全体を巻き添えにしていたことになります。しかも PDF 自体は出来上がるため、**開いてみるまで誰も気付けません**。今は該当項目だけを飛ばし、どの見出しが原因かをログで知らせます
14
+ - **想定外の型が項目に混ざると、アウトライン付与が止まっていた問題を直しました**(`TypeError`)
15
+ - **必要な Ruby を 4.0 以上から 3.4 以上へ緩めました**(コード変更はゼロ、gemspec 1 行のみ)。本体の緩和に合わせたものです。本体だけ緩めても、プラグインが 4.0 以上のままでは 3.4 環境に入りません
74
16
 
75
- `config/book.yml` `pdf_read` セクションで挙動を制御:
17
+ あわせて、これらを二度と起こさないための**堅牢性テスト 14 件**を `test/robustness/` に追加しました。守る約束は 2 つです。
76
18
 
77
- ```yaml
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
- ```
19
+ 1. **公開 API は例外を外へ出さない。** 本体のビルドパイプラインの途中から呼ばれるため、1 つの PDF 操作が例外で飛ぶとビルド全体が落ちます。失敗は戻り値で伝えます
20
+ 2. **失敗しても入力を壊さない。** 読み込んだのと同じパスへ書き戻すので、途中で失敗して元 PDF が切り詰められると「ビルドは続いたのに成果物が壊れている」という最も気付きにくい事故になります
93
21
 
94
- ## テスト品質
22
+ 上の 2 件は、このテストを書く過程で見つかりました。
95
23
 
96
- - **網羅的なテストスイート**
97
- - **実環境での動作検証**
98
- - **外部ツール連携のテスト**
24
+ ## 1.1.1 — 2026-06-23
99
25
 
100
- ## 🔄 0.1.0 からの変更点
26
+ - **隠しノンブルを埋め込み可能なフォントで描画するようにしました(FT-02)。** HexaPDF 組込の標準 14 フォント `Helvetica` は PDF に埋め込まれず、印刷所入稿で「非埋め込みフォント」の差し戻しになっていました
27
+ - 書籍プロジェクト直下に同梱の `stylesheets/fonts/hackgen35/HackGen35ConsoleNF-Regular.ttf` があれば、数字グリフのみをサブセット埋め込みします(増分は極小)。無ければ従来どおり `Helvetica` にフォールバックしてビルドを続けます
101
28
 
102
- ### 新機能
103
- - 完全な OCR 連携と日本語対応
104
- - 画像位置合わせの精密化
105
- - PDF アウトライン生成機能
106
- - 隠しノンブル機能
29
+ ## 1.1.0 — 2026-06-10
107
30
 
108
- ### 改善
109
- - gemspec の RubyGems 公開設定
110
- - ドキュメントの整備
111
- - 外部ツールの自動案内
31
+ - **名前空間を `VivlioStarter::Pdf` へ統一し、`lib/vivlio_starter/` 配下へ再編しました。** 1.0.0 以降で**唯一の非互換変更**です
112
32
 
113
- ## 🎯 実績
33
+ 本体(`vs`)経由で使っている場合、影響はありません。**Ruby API を直接呼んでいる場合のみ**書き換えが必要です。
114
34
 
115
- - **vivlio-starter** Enhanced Mode で実稼働
116
- - **日本語出版** 向けの最適化完了
117
- - **HexaPDF** との高度な連携実現
118
- - **AGPL-3.0** ライセンス準拠
35
+ | | 1.0.x | 1.1.0 以降 |
36
+ |:---|:---|:---|
37
+ | require | `require "vivlio/starter/pdf"` | `require "vivlio_starter/pdf"` |
38
+ | 名前空間 | `Vivlio::Starter::PDF` と `Vivlio::Starter::Pdf` が混在 | `VivlioStarter::Pdf` に統一 |
119
39
 
120
- ## 📋 互換性
40
+ 1.0.x では `PDF` と `Pdf` の 2 つの綴りが同居しており、どちらを書けばよいか実物を見ないと分からない状態でした。
121
41
 
122
- - **Ruby**: 4.0+
123
- - **依存**: hexapdf (~> 1.0), ruby-vips (~> 2.2)
124
- - **外部ツール**: Tesseract, poppler, libvips(OCR 使用時)
125
- - **セマンティックバージョニング**: 1.x.x 系は後方互換性を保証
42
+ ## 1.0.1 — 2026-03-21
126
43
 
127
- ## 🙏 感謝
44
+ - **CLI を追加しました**(`vivlio-starter-pdf --version` / `--help`)。インストール直後に動作確認できます
128
45
 
129
- vivlio-starter-pdf の開発にあたり、HexaPDF の強力な PDF 処理能力と、日本語出版の現場でのニーズが大きな助けとなりました。特に OCR 連携と日本語テキスト処理についての知見は、本プラグインをより実用的なものにする上で不可欠でした。
46
+ ## 1.0.0 2026-03-21
130
47
 
131
- ---
48
+ 最初のメジャーリリースです。
132
49
 
133
- **vivlio-starter-pdf 1.0.0**: 高度な PDF 処理のための強力な拡張プラグイン
50
+ - **隠しノンブル** 入稿用 PDF の塗り足し領域に、断裁されて消えるページ番号を刷り込みます
51
+ - **PDF アウトライン** — 見出しから PDF のブックマークツリーを構築します
52
+ - **PDF → Markdown 変換** — HexaPDF でテキスト・画像を座標付きで抽出します
53
+ - **OCR 連携** — スキャン PDF を自動検出し、Tesseract で日本語 OCR を実行。空白圧縮・括弧正規化・prh 辞書による誤読修正まで行います
54
+ - **画像抽出** — PDF 内の XObject を解析し、WebP 形式で書き出します
55
+ - プラグイン専用設計。インストールされていれば本体が自動検出し、Standard Mode から Enhanced Mode へ切り替わります
data/Rakefile CHANGED
@@ -3,12 +3,59 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
5
 
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << "test"
8
- t.libs << "lib"
9
- t.test_files = FileList["test/**/*_test.rb"]
6
+ # gemspec が Ruby 3.4 以上を謳う以上、それが本当かは実際に走らせないと分からない。
7
+ # `it`(暗黙ブロック引数)は 3.3 以下でも構文エラーにならず「it というメソッドの
8
+ # 呼び出し」として通り、実行時に NameError になる——静的解析では捕まらない。
9
+ SUPPORTED_RUBY_VERSIONS = %w[3.4.10 4.0.6].freeze
10
+
11
+ # 別の Ruby を子プロセスで起動する以上、親の bundler 環境は必ず捨てる。
12
+ # `bundle exec rake test` から呼ばれると、親(現在の Ruby)の bundler を
13
+ # 子(別の Ruby)が読みに行き、テストが始まる前に LoadError で落ちる。
14
+ # nil を渡すと、その環境変数は子から取り除かれる。
15
+ UNBUNDLED_ENV = {
16
+ "RUBYOPT" => nil, "RUBYLIB" => nil, "GEM_HOME" => nil, "GEM_PATH" => nil,
17
+ "BUNDLE_GEMFILE" => nil, "BUNDLE_BIN_PATH" => nil, "BUNDLER_VERSION" => nil,
18
+ "BUNDLER_SETUP" => nil
19
+ }.freeze
20
+
21
+ namespace :test do
22
+ # テストの実体。現在の Ruby で 1 回だけ走る(test:versions が各版でこれを呼ぶ)
23
+ Rake::TestTask.new(:current) do |t|
24
+ t.libs << "test"
25
+ t.libs << "lib"
26
+ t.test_files = FileList["test/**/*_test.rb"]
27
+ end
28
+
29
+ desc "対応する全 Ruby 版(#{SUPPORTED_RUBY_VERSIONS.join(' / ')})でテストする"
30
+ task :versions do
31
+ missing = SUPPORTED_RUBY_VERSIONS - `rbenv versions --bare`.split("\n")
32
+ unless missing.empty?
33
+ abort <<~MESSAGE
34
+ 次の Ruby が rbenv に入っていません: #{missing.join(', ')}
35
+ #{missing.map { "rbenv install #{it}" }.join("\n ")}
36
+ MESSAGE
37
+ end
38
+
39
+ # 版ごとに bundle が要る。未導入なら黙って入れる(その版の初回だけ時間がかかる)
40
+ failed = SUPPORTED_RUBY_VERSIONS.reject do |version|
41
+ puts "\n=== Ruby #{version} ==="
42
+ env = UNBUNDLED_ENV.merge("RBENV_VERSION" => version)
43
+
44
+ unless system(env, "rbenv exec bundle check", out: File::NULL, err: File::NULL)
45
+ next false unless system(env, "rbenv exec bundle install --quiet")
46
+ end
47
+
48
+ system(env, "rbenv exec bundle exec rake test:current")
49
+ end
50
+
51
+ abort "\n失敗した Ruby: #{failed.join(', ')}" unless failed.empty?
52
+ puts "\n全 Ruby 版で通過: #{SUPPORTED_RUBY_VERSIONS.join(' / ')}"
53
+ end
10
54
  end
11
55
 
56
+ # 手元では常に対応全版を回す(1 版あたり 10 秒弱で終わるため、分けるほどの負担にならない)。
57
+ # CI(GitHub Actions)は版をマトリクスで分担するので、各ジョブは 1 回でよい。
58
+ task test: (ENV["CI"] ? "test:current" : "test:versions")
12
59
  task default: :test
13
60
 
14
61
  # gem のアンインストール → ビルド → インストールを一括実行
@@ -16,7 +63,7 @@ task :reinstall do
16
63
  gemspec = Dir['*.gemspec'].first
17
64
  raise 'gemspec が見つかりません' unless gemspec
18
65
 
19
- require_relative 'lib/vivlio_starter/pdf/version'
66
+ require_relative 'lib/vivlio_starter/cli/pdf/version'
20
67
  version = VivlioStarter::Pdf::VERSION
21
68
  gem_name = 'vivlio-starter-pdf'
22
69
  gem_file = "#{gem_name}-#{version}.gem"
@@ -54,7 +54,9 @@ module VivlioStarter
54
54
 
55
55
  # Normalizes a raw hash into a consistent entry structure.
56
56
  def normalize_entry(raw)
57
- text = fetch(raw, :text, fetch(raw, :title, nil)).to_s.strip
57
+ text = entry_title(raw)
58
+ return nil if text.nil?
59
+
58
60
  warn_skip("missing title for outline entry: #{raw.inspect}") if text.empty?
59
61
  return nil if text.empty?
60
62
 
@@ -71,13 +73,40 @@ module VivlioStarter
71
73
  { text:, page:, level: }
72
74
  end
73
75
 
76
+ # Returns the trimmed title, or +nil+ when the entry must be skipped.
77
+ #
78
+ # A title carrying invalid UTF-8 bytes makes +String#strip+ raise
79
+ # +Encoding::CompatibilityError: invalid byte sequence in UTF-8+. Letting that
80
+ # escape costs the *entire* outline: +add_outline!+ rescues at the top, so one
81
+ # mojibake heading would leave the book with no bookmarks at all. Every other
82
+ # kind of bad entry is skipped one by one through +on_skip+ — this one must
83
+ # behave the same way, and the message has to name the entry so the author can
84
+ # fix the manuscript heading that produced it.
85
+ #
86
+ # Both +EncodingError+ and +ArgumentError+ are caught on purpose: which one
87
+ # Ruby raises for broken bytes depends on the method (+strip+ raises
88
+ # +Encoding::CompatibilityError+, +match?+ raises +ArgumentError+), and that
89
+ # detail must not decide whether the book keeps its bookmarks.
90
+ def entry_title(raw)
91
+ fetch(raw, :text, fetch(raw, :title, nil)).to_s.strip
92
+ rescue EncodingError, ArgumentError
93
+ # +inspect+ is safe here: it escapes invalid bytes (\xFF) instead of raising.
94
+ warn_skip("title is not valid UTF-8, skipping outline entry: #{raw.inspect}")
95
+ nil
96
+ end
97
+
74
98
  # Fetches a value from the entry by symbol or string key.
99
+ #
100
+ # Entries come from the caller, so a stray String or Integer can end up in the
101
+ # array. Both answer +respond_to?(:[])+ but raise +TypeError+ when indexed with
102
+ # a Symbol (+Integer#[]+ is bit access), which would abort the whole outline —
103
+ # exactly what the per-entry skip exists to prevent. Duck typing is kept so a
104
+ # Hash-like object still works; only the indexing failure falls back.
75
105
  def fetch(raw, key, fallback)
76
- if raw.respond_to?(:[]) && (raw[key] || raw[key.to_s])
77
- raw[key] || raw[key.to_s]
78
- else
79
- fallback
80
- end
106
+ value = (raw[key] || raw[key.to_s] if raw.respond_to?(:[]))
107
+ value.nil? ? fallback : value
108
+ rescue TypeError
109
+ fallback
81
110
  end
82
111
 
83
112
  # Returns the proper parent node for the requested level.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module VivlioStarter
4
4
  module Pdf
5
- VERSION = "1.1.1"
5
+ VERSION = "1.1.2"
6
6
  end
7
7
  end
@@ -5,18 +5,20 @@ require_relative "lib/vivlio_starter/cli/pdf/version"
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "vivlio-starter-pdf"
7
7
  spec.version = VivlioStarter::Pdf::VERSION
8
- spec.authors = ["Mirai"]
9
- spec.email = ["mirai@example.com"]
8
+ spec.authors = ["Atelier Mirai"]
9
+ spec.email = ["contact@atelier-mirai.net"]
10
10
 
11
11
  spec.summary = "Advanced PDF processor for vivlio-starter using HexaPDF"
12
12
  spec.description = "Provides PDF outline extraction, precision page numbering, and OCR via HexaPDF. AGPL-3.0 licensed."
13
13
  spec.homepage = "https://github.com/Atelier-Mirai/vivlio-starter-pdf"
14
14
  spec.license = "AGPL-3.0-only"
15
- spec.required_ruby_version = ">= 4.0.0"
15
+ spec.required_ruby_version = ">= 3.4.0"
16
16
 
17
+ # homepage_uri は spec.homepage から自動で作られる。metadata にも同じ URL を書くと
18
+ # rubygems.org はどちらか片方しか表示せず、gem build が警告を出す
17
19
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
- spec.metadata["homepage_uri"] = spec.homepage
19
20
  spec.metadata["source_code_uri"] = "https://github.com/Atelier-Mirai/vivlio-starter-pdf"
21
+ spec.metadata["changelog_uri"] = "https://github.com/Atelier-Mirai/vivlio-starter-pdf/blob/master/CHANGELOG.md"
20
22
  spec.post_install_message = <<~MESSAGE
21
23
  [vivlio-starter-pdf]
22
24
  Enhanced Mode の OCR には外部ツール(tesseract / tesseract-lang / poppler / vips)が必要です。
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vivlio-starter-pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
- - Mirai
7
+ - Atelier Mirai
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
@@ -82,12 +82,13 @@ dependencies:
82
82
  description: Provides PDF outline extraction, precision page numbering, and OCR via
83
83
  HexaPDF. AGPL-3.0 licensed.
84
84
  email:
85
- - mirai@example.com
85
+ - contact@atelier-mirai.net
86
86
  executables:
87
87
  - vivlio-starter-pdf
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
+ - ".ruby-version"
91
92
  - CHANGELOG.md
92
93
  - LICENSE
93
94
  - README.md
@@ -107,8 +108,8 @@ licenses:
107
108
  - AGPL-3.0-only
108
109
  metadata:
109
110
  allowed_push_host: https://rubygems.org
110
- homepage_uri: https://github.com/Atelier-Mirai/vivlio-starter-pdf
111
111
  source_code_uri: https://github.com/Atelier-Mirai/vivlio-starter-pdf
112
+ changelog_uri: https://github.com/Atelier-Mirai/vivlio-starter-pdf/blob/master/CHANGELOG.md
112
113
  post_install_message: |
113
114
  [vivlio-starter-pdf]
114
115
  Enhanced Mode の OCR には外部ツール(tesseract / tesseract-lang / poppler / vips)が必要です。
@@ -124,14 +125,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
125
  requirements:
125
126
  - - ">="
126
127
  - !ruby/object:Gem::Version
127
- version: 4.0.0
128
+ version: 3.4.0
128
129
  required_rubygems_version: !ruby/object:Gem::Requirement
129
130
  requirements:
130
131
  - - ">="
131
132
  - !ruby/object:Gem::Version
132
133
  version: '0'
133
134
  requirements: []
134
- rubygems_version: 4.0.14
135
+ rubygems_version: 4.0.16
135
136
  specification_version: 4
136
137
  summary: Advanced PDF processor for vivlio-starter using HexaPDF
137
138
  test_files: []