Module: Vivlio::Starter::CLI::Build::PdfMerger
- Defined in:
- lib/vivlio/starter/cli/build/pdf_merger.rb
Class Method Summary collapse
-
.add_outline_to_output_pdf!(entries_or_keep = nil) ⇒ Object
5.
-
.cover_enhanced_files ⇒ Object
1.
- .cover_generation_attempts ⇒ Object
-
.ensure_cover_assets_for_page_size!(page_use) ⇒ Object
3.
- .extract_size_from_preset(preset_name) ⇒ Object
-
.extract_targets(raw) ⇒ Object
2.
-
.insert_blank_page_before_colophon(files) ⇒ Object
奥付が偶数ページ(左ページ)始まりになるよう空白ページを挿入 _colophon.pdf(閲覧用)と _colophon_print.pdf(入稿用)の両方に対応.
-
.merge_all_pdfs!(_entries_or_keep = nil) ⇒ Object
4.
- .qpdf_available? ⇒ Boolean
- .resolve_page_use(page_cfg) ⇒ Object
Class Method Details
.add_outline_to_output_pdf!(entries_or_keep = nil) ⇒ Object
-
アウトライン付与 (Step 11)
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 183 def add_outline_to_output_pdf!(entries_or_keep = nil) return false unless File.exist?('output.pdf') keep_numbers = Build::Utilities.chapter_numbers_for_outline(entries_or_keep) # 抽出対象HTMLの絞り込み special_pages = %w[_toc] special_pages.push('_glossarypage', '_indexpage') if IndexCommands.index_enabled? chapter_htmls = Dir.glob('*.html').select do |path| bn = File.basename(path, '.html') num = bn[/\A(\d+)-/, 1]&.to_i (num && (keep_numbers.nil? || keep_numbers.include?(num))) || special_pages.include?(bn) end if chapter_htmls.empty? Common.log_info('[Step 11] 本文HTMLなし。スキップします') return false end Common.log_action('[Step 11] PDF ブックマークを付与します…') OutlineExtractor.add_outline_from_headings!('output.pdf', chapter_htmls, max_level: 3, start_page: 1) true end |
.cover_enhanced_files ⇒ Object
-
結合対象ファイルのリスト作成
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 16 def cover_enhanced_files files = %w[_titlepage_legalpage.pdf _sections.pdf _colophon.pdf] cfg = Common::CONFIG # ターゲット判定(dig で安全アクセス) targets = extract_targets(cfg.dig(:output, :targets)) targets = extract_targets(cfg.dig(:output, :pdf, :targets)) if targets.empty? pdf_selected = targets.empty? || targets.any? { it.include?('pdf') } return files.compact unless pdf_selected # 新しいカバー設定の取得 return files.compact unless Common.pdf_combined? begin page_use = resolve_page_use(cfg.page) covers_dir = cfg.directories&.covers || 'covers' ensure_cover_assets_for_page_size!(page_use) # テーマに応じたカバーを生成 theme = Common.cover_theme size = extract_size_from_preset(page_use) # パス生成 front = File.join(covers_dir, "frontcover_#{theme}_#{size}_rgb.pdf") back = File.join(covers_dir, "backcover_#{theme}_#{size}_rgb.pdf") files.unshift(front) if File.exist?(front) files.push(back) if File.exist?(back) rescue StandardError => e Common.log_warn("[Step 10] カバー結合設定の処理中にエラー: #{e.}") end files.compact end |
.cover_generation_attempts ⇒ Object
99 100 101 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 99 def cover_generation_attempts @cover_generation_attempts ||= {} end |
.ensure_cover_assets_for_page_size!(page_use) ⇒ Object
-
カバー自動生成ロジック
86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 86 def ensure_cover_assets_for_page_size!(page_use) size = CoverCommands.detect_page_size(page_use) return if cover_generation_attempts[size] cover_generation_attempts[size] = true Common.log_action('[Step 10] カバー画像を自動生成します…') CoverCommands.ensure_cover_files_for_build! Common.log_info('[Step 10] カバー画像の生成を完了しました') rescue StandardError => e Common.log_warn("[Step 10] カバー生成中にエラー: #{e.}") end |
.extract_size_from_preset(preset_name) ⇒ Object
74 75 76 77 78 79 80 81 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 74 def extract_size_from_preset(preset_name) case preset_name.to_s when /a4/ then 'a4' when /a5/ then 'a5' when /b5/ then 'b5' else 'a4' # デフォルト end end |
.extract_targets(raw) ⇒ Object
-
補助メソッド (Data / Pattern Matching 活用)
57 58 59 60 61 62 63 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 57 def extract_targets(raw) case raw in String => s then s.split(',').map(&:strip).reject(&:empty?) in Array => a then a.map(&:to_s).map(&:strip).reject(&:empty?) else [] end end |
.insert_blank_page_before_colophon(files) ⇒ Object
奥付が偶数ページ(左ページ)始まりになるよう空白ページを挿入_colophon.pdf(閲覧用)と _colophon_print.pdf(入稿用)の両方に対応
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 141 def insert_blank_page_before_colophon(files) colophon_idx = files.index { it.include?('_colophon') } return files unless colophon_idx preceding = files[0...colophon_idx] # カバーPDFはページ番号体系に含まれないため parity 計算から除外 body_files = preceding.grep_v(%r{covers/}) # 各PDFのページ数を個別に取得してログ出力(デバッグ時のみ) page_counts = body_files.map { |f| [f, Build::Utilities.page_count(f).to_i] } page_counts.each { |f, c| Common.log_debug("[Step 10] ページ数: #{f} = #{c}p") } total = page_counts.sum(&:last) Common.log_debug("[Step 10] 奥付前の合計ページ数(カバー除外): #{total}") if total.zero? Common.log_debug('[Step 10] 奥付より前のPDFページ数を取得できませんでした') return files end # total が偶数 → 次ページは奇数(右) → 空白ページを挿入して偶数に # total が奇数 → 次ページは偶数(左) → そのままでOK if total.even? blank = Build::Utilities.ensure_blank_page_pdf('_blank_before_colophon.pdf') Common.log_debug("[Step 10] 奥付を偶数ページに配置するため空白ページを挿入します(前方 #{total} ページ)") files.dup.insert(colophon_idx, blank) else Common.log_debug("[Step 10] 奥付は偶数ページに配置されます(前方 #{total} ページ、空白挿入なし)") files end end |
.merge_all_pdfs!(_entries_or_keep = nil) ⇒ Object
-
PDF 結合実行 (Step 10)
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 106 def merge_all_pdfs!(_entries_or_keep = nil) Common.log_action('[Step 10] 表紙、本文、奥付を結合します…') files = cover_enhanced_files existing_files = files.select { File.exist?(it) } if existing_files.empty? Common.log_error('[Step 10] 結合対象PDFがありません') return false end return false unless qpdf_available? # 奥付を偶数ページ(左ページ)に配置するため、必要なら空白ページを挿入 existing_files = insert_blank_page_before_colophon(existing_files) # _sections.pdf があればそれをベースに、なければ最初のファイルを使用 base_pdf = existing_files.include?('_sections.pdf') ? '_sections.pdf' : existing_files.first FileUtils.rm_f('output.pdf') # 引数構築 ranges = existing_files.map { %("#{it}" 1-z) }.join(' ') success = system(%(qpdf "#{base_pdf}" --pages #{ranges} -- "output.pdf" > /dev/null)) if success && File.exist?('output.pdf') Common.log_success('[Step 10] output.pdf を生成しました') true else Common.log_error('[Step 10] PDF結合に失敗しました') false end end |
.qpdf_available? ⇒ Boolean
173 174 175 176 177 178 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 173 def qpdf_available? return true if system('command -v qpdf >/dev/null 2>&1') Common.log_warn('[Step 10] qpdf が見つかりません。') false end |
.resolve_page_use(page_cfg) ⇒ Object
65 66 67 68 69 70 71 72 |
# File 'lib/vivlio/starter/cli/build/pdf_merger.rb', line 65 def resolve_page_use(page_cfg) # Data オブジェクトからプリセット名を優先順位付きで取得 %i[use preset preset_name size].each do |key| val = page_cfg&.[](key) return val.to_s if val && !val.to_s.strip.empty? end 'b5_standard' end |