Module: VivlioStarter::CLI::Build::CmykConverter
- Defined in:
- lib/vivlio_starter/cli/build/cmyk_converter.rb
Overview
================================================================
Build::CmykConverter — 表紙 CMYK カラーマネジメント(cover-cmyk spec)
RGB の表紙 PDF を Japan Color 2001 Coated ICC で CMYK 変換し、出力インテントを
埋め込んだ PDF/X-1a:2001 として書き出す。従来の素朴な magick -colorspace CMYK
(ICC なし・出力インテントなし=くすみ・ビューア解釈不一致)を置き換える。
役割分担:
- 色変換+PDF/X-1a 化: Ghostscript(ICC 知覚的レンダリング+出力インテント埋込)。
- ジオメトリ(TrimBox/BleedBox)確定: Build::PrintGeometry.finalize_boxes!
(qpdf・構造保存)。gs は pdfwrite で TrimBox を MediaBox にリセットしてしまい、
CombinePDF の再保存は OutputIntent を落とすため、qpdf 差分更新で箱だけ書く。
ICC 入手: @vivliostyle/cli(必須依存)が推移的に持つ press-ready 同梱の
JapanColor2001Coated.icc を利用する(gem への ICC 再配布は不要)。
設定キー output.print_pdf.icc_profile でユーザー指定パスも許可する。
gs は AGPL だが CLI としてサブプロセス起動するだけ(本体 MIT・圧縮でも既に使用)。
Constant Summary collapse
- PRESS_READY_ICC =
press-ready 同梱 ICC の node_modules 相対パス(@vivliostyle/cli の推移的依存で常在)
'node_modules/press-ready/assets/JapanColor2001Coated.icc'- OUTPUT_CONDITION =
出力インテントの条件識別子・出力条件(Japan Color 2001 Coated)
'Japan Color 2001 Coated'
Class Method Summary collapse
-
.available? ⇒ Boolean
ICC ベースの CMYK 変換が可能か(ICC が解決できるか)。.
-
.icc_profile_path ⇒ Object
使用する ICC プロファイルの絶対パスを解決する。 優先順: 設定キー output.print_pdf.icc_profile(ユーザー指定)→ press-ready 同梱。 いずれも無ければ nil(呼び出し側は従来の素朴 CMYK へフォールバック)。 メモ化しないのは、解決が cwd 相対(node_modules 探索)で安価なため (プロセス内で cwd を跨ぐテストの汚染を避ける・ビルド毎の呼び出しは数回)。.
-
.pdfx_def_ps(icc, title) ⇒ Object
PDF/X-1a:2001 の出力インテント定義(ICC を stream 埋込・CMYK=4 成分)。 gs 同梱 PDFX_def.ps を最小化したもの。-dPDFX=1 と併用する。.
-
.ps_escape(str) ⇒ Object
PostScript 文字列リテラル用のエスケープ(\ ( ) を順にエスケープ)。.
-
.run_gs_pdfx!(input, output, icc, defs) ⇒ Object
gs で RGB→CMYK+PDF/X-1a 化を実行する。 --permit-file-read で SAFER を維持したまま ICC の読み取りだけ許可する (gs 10.x の SAFER 既定では ICC 読み取りが拒否されるため。press-ready 4.x が gs 10.x で失敗するのと同じ落とし穴を回避)。.
-
.to_pdfx!(pdf_path, bleed_mm:, crop_offset_mm:, title: 'Cover') ⇒ Boolean
RGB 表紙 PDF を CMYK PDF/X-1a:2001(Japan Color 2001 Coated 出力インテント)へ 変換し、TrimBox/BleedBox を確定する。成功時は pdf_path を上書きする。.
Class Method Details
.available? ⇒ Boolean
ICC ベースの CMYK 変換が可能か(ICC が解決できるか)。
39 |
# File 'lib/vivlio_starter/cli/build/cmyk_converter.rb', line 39 def available? = !icc_profile_path.nil? |
.icc_profile_path ⇒ Object
使用する ICC プロファイルの絶対パスを解決する。 優先順: 設定キー output.print_pdf.icc_profile(ユーザー指定)→ press-ready 同梱。 いずれも無ければ nil(呼び出し側は従来の素朴 CMYK へフォールバック)。 メモ化しないのは、解決が cwd 相対(node_modules 探索)で安価なため (プロセス内で cwd を跨ぐテストの汚染を避ける・ビルド毎の呼び出しは数回)。
46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/vivlio_starter/cli/build/cmyk_converter.rb', line 46 def icc_profile_path configured = Common::CONFIG&.dig(:output, :print_pdf, :icc_profile) if configured && !configured.to_s.strip.empty? path = File.(configured.to_s) return path if File.exist?(path) Common.log_warn("指定された ICC プロファイルが見つかりません: #{configured}") end bundled = File.(PRESS_READY_ICC) File.exist?(bundled) ? bundled : nil end |
.pdfx_def_ps(icc, title) ⇒ Object
PDF/X-1a:2001 の出力インテント定義(ICC を stream 埋込・CMYK=4 成分)。 gs 同梱 PDFX_def.ps を最小化したもの。-dPDFX=1 と併用する。
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/vivlio_starter/cli/build/cmyk_converter.rb', line 107 def pdfx_def_ps(icc, title) <<~PS %! systemdict /PDFX known {systemdict /PDFX get}{1} ifelse dup 1 eq { [ /GTS_PDFXVersion (PDF/X-1a:2001) /Title (#{ps_escape(title)}) /Trapped /False /DOCINFO pdfmark } if pop /ICCProfile (#{ps_escape(icc)}) def [/_objdef {icc_PDFX} /type /stream /OBJ pdfmark [{icc_PDFX} << /N 4 >> /PUT pdfmark [{icc_PDFX} ICCProfile (r) file /PUT pdfmark [/_objdef {OutputIntent_PDFX} /type /dict /OBJ pdfmark [{OutputIntent_PDFX} << /Type /OutputIntent /S /GTS_PDFX /OutputCondition (#{ps_escape(OUTPUT_CONDITION)}) /OutputConditionIdentifier (#{ps_escape(OUTPUT_CONDITION)}) /RegistryName (http://www.color.org) /DestOutputProfile {icc_PDFX} >> /PUT pdfmark [{Catalog} << /OutputIntents [ {OutputIntent_PDFX} ] >> /PUT pdfmark PS end |
.ps_escape(str) ⇒ Object
PostScript 文字列リテラル用のエスケープ(\ ( ) を順にエスケープ)。
131 |
# File 'lib/vivlio_starter/cli/build/cmyk_converter.rb', line 131 def ps_escape(str) = str.to_s.gsub('\\', '\\\\\\\\').gsub('(', '\\(').gsub(')', '\\)') |
.run_gs_pdfx!(input, output, icc, defs) ⇒ Object
gs で RGB→CMYK+PDF/X-1a 化を実行する。 --permit-file-read で SAFER を維持したまま ICC の読み取りだけ許可する (gs 10.x の SAFER 既定では ICC 読み取りが拒否されるため。press-ready 4.x が gs 10.x で失敗するのと同じ落とし穴を回避)。
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/vivlio_starter/cli/build/cmyk_converter.rb', line 94 def run_gs_pdfx!(input, output, icc, defs) cmd = [ 'gs', '-dBATCH', '-dNOPAUSE', '-dPDFX=1', '-dNOOUTERSAVE', "--permit-file-read=#{icc}", '-sDEVICE=pdfwrite', '-sColorConversionStrategy=CMYK', "-sOutputICCProfile=#{icc}", '-o', output, defs, input ] system(*cmd, out: File::NULL, err: File::NULL) && File.exist?(output) && File.size(output).positive? end |
.to_pdfx!(pdf_path, bleed_mm:, crop_offset_mm:, title: 'Cover') ⇒ Boolean
RGB 表紙 PDF を CMYK PDF/X-1a:2001(Japan Color 2001 Coated 出力インテント)へ 変換し、TrimBox/BleedBox を確定する。成功時は pdf_path を上書きする。
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/vivlio_starter/cli/build/cmyk_converter.rb', line 67 def to_pdfx!(pdf_path, bleed_mm:, crop_offset_mm:, title: 'Cover') icc = icc_profile_path return false unless icc && File.exist?(pdf_path) converted = Dir.mktmpdir('cmyk-pdfx') do |tmp| defs = File.join(tmp, 'PDFX_def.ps') File.write(defs, pdfx_def_ps(icc, title)) out = File.join(tmp, 'out.pdf') next false unless run_gs_pdfx!(pdf_path, out, icc, defs) FileUtils.mv(out, pdf_path) true end return false unless converted # gs は箱を MediaBox にリセットするため、qpdf(構造保存)で TrimBox/BleedBox を確定。 Build::PrintGeometry.finalize_boxes!(pdf_path, bleed_mm:, crop_offset_mm:) true rescue StandardError => e Common.log_warn("[cover cmyk] PDF/X-1a 変換に失敗しました: #{e.}") false end |