Class: VivlioStarter::CLI::PreProcessCommands::ShowcaseTransformer::ImageTools
- Inherits:
-
Object
- Object
- VivlioStarter::CLI::PreProcessCommands::ShowcaseTransformer::ImageTools
- Defined in:
- lib/vivlio_starter/cli/pre_process/showcase_transformer.rb
Overview
外部ツール依存をこのクラスへ隔離する(builder は純関数のまま保つ・§7.1)。
Instance Method Summary collapse
-
#available? ⇒ Boolean
magick と rsvg-convert が両方揃っているか。.
-
#data_uri(path) ⇒ Object
元画像を base64 data URI へ。EMBED_MAX_EDGE まで縮小して軽量化する (viewBox は元画像実寸で張るため、縮小しても座標計算には影響しない)。.
-
#image_dimensions(path) ⇒ Array(Integer, Integer)?
元画像の実寸(px→パーミル変換・viewBox 計算に使う)。.
-
#photographic?(path) ⇒ Boolean
元画像が写真か(=ユニーク色数が多いか)。スクリーンショットは平坦色で色数が 桁違いに少ない(冒頭コメントの実測値)。判定はキャッシュ未ヒット時のみ走り、 実測 0.2〜0.35 秒。.
-
#rasterize(svg, width, format: :png) ⇒ Object
合成 SVG をラスター(バイト列)へ変換する。JPEG は透過を持てないため白で フラット化する(リーダーの白ページと馴染ませる。HeadingImageComposer と同じ流儀)。.
Instance Method Details
#available? ⇒ Boolean
magick と rsvg-convert が両方揃っているか。
287 288 289 290 291 |
# File 'lib/vivlio_starter/cli/pre_process/showcase_transformer.rb', line 287 def available? return @available unless @available.nil? @available = tool_ok?('magick', '-version') && tool_ok?('rsvg-convert', '--version') end |
#data_uri(path) ⇒ Object
元画像を base64 data URI へ。EMBED_MAX_EDGE まで縮小して軽量化する (viewBox は元画像実寸で張るため、縮小しても座標計算には影響しない)。
写真は JPEG で埋める。 PNG は可逆なので、写真を埋めると PDF 内でも
Flate のまま展開され、1 枚あたり 1,849 KB を占めていた(実測 2026-08-17・
全章で 5 枚 9.2 MB。image-format-per-target-spec.md §4.2)。判定は EPUB 向けの
ラスター形式と同じ photographic? を使う——同じ絵に別の基準を持つ理由がない。
透過は白へ落とす(rasterize の JPEG 枝と同じ流儀。SVG の地はページの白)。
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/vivlio_starter/cli/pre_process/showcase_transformer.rb', line 325 def data_uri(path) resize = ['-resize', "#{EMBED_MAX_EDGE}x#{EMBED_MAX_EDGE}>"] if photographic?(path) jpg, jpg_status = Open3.capture2('magick', path, *resize, '-background', 'white', '-alpha', 'remove', '-alpha', 'off', '-quality', '90', 'jpg:-', binmode: true) return "data:image/jpeg;base64,#{[jpg].pack('m0')}" if jpg_status.success? && !jpg.empty? end png, status = Open3.capture2('magick', path, *resize, 'png:-', binmode: true) return nil unless status.success? && !png.empty? # base64 は Ruby 3.4+ で default gem 外のため Array#pack('m0')(改行なし base64)で代替 "data:image/png;base64,#{[png].pack('m0')}" rescue StandardError nil end |
#image_dimensions(path) ⇒ Array(Integer, Integer)?
元画像の実寸(px→パーミル変換・viewBox 計算に使う)。
295 296 297 298 299 300 301 302 303 |
# File 'lib/vivlio_starter/cli/pre_process/showcase_transformer.rb', line 295 def image_dimensions(path) out, status = Open3.capture2('magick', 'identify', '-format', '%w %h', path) return nil unless status.success? width, height = out.split.map(&:to_i) (width.to_i.positive? && height.to_i.positive?) ? [width, height] : nil rescue StandardError nil end |
#photographic?(path) ⇒ Boolean
元画像が写真か(=ユニーク色数が多いか)。スクリーンショットは平坦色で色数が 桁違いに少ない(冒頭コメントの実測値)。判定はキャッシュ未ヒット時のみ走り、 実測 0.2〜0.35 秒。
308 309 310 311 312 313 314 315 |
# File 'lib/vivlio_starter/cli/pre_process/showcase_transformer.rb', line 308 def photographic?(path) out, status = Open3.capture2('magick', 'identify', '-format', '%k', path) return false unless status.success? out.strip.to_i > PHOTO_COLOR_THRESHOLD rescue StandardError false end |
#rasterize(svg, width, format: :png) ⇒ Object
合成 SVG をラスター(バイト列)へ変換する。JPEG は透過を持てないため白で フラット化する(リーダーの白ページと馴染ませる。HeadingImageComposer と同じ流儀)。
348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/vivlio_starter/cli/pre_process/showcase_transformer.rb', line 348 def rasterize(svg, width, format: :png) png, status = Open3.capture2('rsvg-convert', '-w', width.to_s, '-f', 'png', stdin_data: svg, binmode: true) return nil unless status.success? && !png.empty? return png if format == :png jpg, jpg_status = Open3.capture2('magick', 'png:-', '-background', 'white', '-flatten', '-quality', '85', 'jpg:-', stdin_data: png, binmode: true) (jpg_status.success? && !jpg.empty?) ? jpg : nil rescue StandardError nil end |