Module: VivlioStarter::CLI::Build::NombreStamper

Defined in:
lib/vivlio_starter/cli/build/nombre_stamper.rb

Constant Summary collapse

MM_TO_PT =
Units::PT_PER_MM
FONT_SIZE =
6
NOMBRE_FONT_RELATIVE =

隠しノンブルに使う埋め込み可能フォント(TTF)。 Prawn の標準 14 フォント 'Helvetica' は PDF に埋め込まれず、印刷所入稿で 「非埋め込みフォント」事故になる(FT-02)。同梱の HackGen35ConsoleNF(TTF)を 使えば Prawn が使用グリフ(数字のみ)をサブセット埋め込みするため極小で済む。

File.join('stylesheets', 'fonts', 'hackgen35', 'HackGen35ConsoleNF-Regular.ttf')

Class Method Summary collapse

Class Method Details

.bleed_mm_from_configFloat

塗り足し幅を book.yml から取得する(mm 単位の数値を返す) "3mm" → 3.0, "5mm" → 5.0, nil → 3.0

Returns:

  • (Float)

    塗り足し幅(mm)



158
159
160
161
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 158

def bleed_mm_from_config
  raw = Common::CONFIG.output.print_pdf.bleed
  parse_bleed_mm(raw)
end

.create_nombre_pdf!(output_path, source_pdf, bleed_pt) ⇒ Object

対象 PDF と同じページ数・同じページサイズで、ノンブルだけを描いた PDF を生成する。 ページサイズを 1 ページずつ元 PDF から読み取るのは、空白ページ挿入や 前付・奥付の混在でページサイズが揃わない場合にも重畳位置をずらさないため。

Parameters:

  • output_path (String)

    出力先

  • source_pdf (String)

    ページ数・ページサイズの参照元

  • bleed_pt (Float)

    塗り足し幅(pt)



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 95

def create_nombre_pdf!(output_path, source_pdf, bleed_pt)
  reader = ::PDF::Reader.new(source_pdf)
  font = nombre_font

  Prawn::Document.generate(output_path, skip_page_creation: true, margin: 0) do |pdf|
    reader.pages.each_with_index do |page, idx|
      box = page.attributes[:MediaBox] || [0, 0, 595.28, 841.89]
      width  = box[2].to_f - box[0].to_f
      height = box[3].to_f - box[1].to_f

      pdf.start_new_page(size: [width, height], margin: 0)
      # フォントはページ作成後に設定する(skip_page_creation のため最初のページ前は不可)。
      # 数字のみのため TTF はサブセット埋め込みされ極小で済む。
      pdf.font(font, size: FONT_SIZE)
      pdf.fill_color('000000')

      draw_page_number(pdf, idx + 1, width, height, bleed_pt)
    end
  end
end

.draw_page_number(pdf, page_number, width, height, bleed_pt) ⇒ Object

個別ページにノンブルを描画する 奇数ページ(右ページ)はノド側 = 左端、偶数ページ(左ページ)はノド側 = 右端



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 127

def draw_page_number(pdf, page_number, width, height, bleed_pt)
  x_offset = bleed_pt / 2.0
  y_center = height / 2.0
  text = page_number.to_s

  if page_number.odd?
    # 右ページ → ノド = 左端、時計回り 90°
    draw_rotated_text(pdf, text, x: x_offset, y: y_center, angle: 90)
  else
    # 左ページ → ノド = 右端、反時計回り -90°
    draw_rotated_text(pdf, text, x: width - x_offset, y: y_center, angle: -90)
  end
end

.draw_rotated_text(pdf, text, x:, y:, angle:) ⇒ Object

テキストを回転して描画する(translate → rotate → draw_text)



142
143
144
145
146
147
148
149
150
151
152
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 142

def draw_rotated_text(pdf, text, x:, y:, angle:)
  text_width  = pdf.width_of(text)
  text_height = pdf.font.height

  pdf.save_graphics_state do
    pdf.translate(x, y)
    pdf.rotate(angle) do
      pdf.draw_text(text, at: [-text_width / 2.0, -text_height / 2.0])
    end
  end
end

.nombre_fontString

隠しノンブルのフォントを解決する。 プロジェクト(cwd)→ gem 同梱 の順に同梱 TTF を探し、いずれも無ければ 'Helvetica'(非埋め込み)へフォールバックして従来どおりビルドは継続する。

Returns:

  • (String)

    Prawn に渡すフォント(TTF パス or 'Helvetica')



120
121
122
123
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 120

def nombre_font
  gem_bundled = File.expand_path(File.join('../../../..', NOMBRE_FONT_RELATIVE), __dir__)
  [NOMBRE_FONT_RELATIVE, gem_bundled].find { File.exist?(it) } || 'Helvetica'
end

.parse_bleed_mm(raw) ⇒ Float

塗り足し文字列を mm 数値に変換する

Parameters:

  • raw (String, Numeric, nil)

    塗り足し幅の生値

Returns:

  • (Float)

    mm 単位の数値(既定: 3.0)



166
167
168
169
170
171
172
173
174
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 166

def parse_bleed_mm(raw)
  case raw
  in NilClass then 3.0
  in Numeric => n then n.to_f
  in String => s
    s.strip.downcase.sub(/mm\z/, '').to_f.then { it.positive? ? it : 3.0 }
  else 3.0
  end
end

.stamp!(pdf_path, bleed_mm: 3) ⇒ Boolean

入稿用 PDF に隠しノンブルを書き込む 結合済み PDF のすべてのページに通し番号(1〜N)を付与する。

Parameters:

  • pdf_path (String)

    対象 PDF のパス(成功時に上書きされる)

  • bleed_mm (Numeric) (defaults to: 3)

    塗り足し幅(mm)。ノンブルの X 座標に使う

Returns:

  • (Boolean)

    書き込み成功なら true



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/vivlio_starter/cli/build/nombre_stamper.rb', line 57

def stamp!(pdf_path, bleed_mm: 3)
  # --- Phase: 検証 ---
  unless File.exist?(pdf_path)
    Common.log_warn("[NombreStamper] PDF が見つかりません: #{pdf_path}")
    return false
  end

  total_pages = Build::Utilities.page_count(pdf_path).to_i
  return false unless total_pages.positive?

  # --- Phase: ノンブル PDF の生成(Prawn) ---
  Common.log_action("[NombreStamper] 隠しノンブルを書き込みます(#{total_pages} ページ)…")
  nombre_pdf = "#{pdf_path}.nombre.pdf"
  create_nombre_pdf!(nombre_pdf, pdf_path, bleed_mm.to_f * MM_TO_PT)

  # --- Phase: 重畳(qpdf・構造保存・等倍) ---
  success = Build::QpdfOverlay.apply!(pdf_path, nombre_pdf)
  FileUtils.rm_f(nombre_pdf)

  if success
    Common.log_success("[NombreStamper] 隠しノンブル書き込み完了(#{total_pages} ページ)")
  else
    Common.log_error('[NombreStamper] 隠しノンブルの重畳(qpdf --overlay)に失敗しました')
  end
  success
rescue StandardError => e
  Common.log_error("[NombreStamper] 隠しノンブル書き込みに失敗: #{e.message}")
  FileUtils.rm_f("#{pdf_path}.nombre.pdf")
  false
end