Module: VivlioStarter::CLI::PreProcessCommands::QrTransformer

Defined in:
lib/vivlio_starter/cli/pre_process/qr_transformer.rb

Overview

@qr:URL を QR コード SVG の へ変換するモジュール。

Constant Summary collapse

REL_BASE =

生成物の出力先(images/ 配下)。

'qr'
QR_PATTERN =

@qr: に続く URL。空白か ) の手前まで(§1.4)。

%r{@qr:(https?://[^\s)]+)}
MODULE_SIZE =

QR の 1 モジュール(黒白の 1 マス)の辺長 px。印刷 18mm 角でも読み取れる解像度。

4
MASK =

インラインコード退避用の番兵(本文に現れない NUL 文字)。

0.chr

Class Method Summary collapse

Class Method Details

.available?Boolean

rqrcode が使えるか(gem 未導入の環境でもビルドは止めない)。

Returns:

  • (Boolean)


130
131
132
133
134
135
136
137
138
139
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 130

def available?
  return @available unless @available.nil?

  @available = begin
    require 'rqrcode'
    true
  rescue LoadError
    false
  end
end

.build_svg(url) ⇒ Object

rqrcode で SVG 本体を組み立て、intrinsic size と viewBox の両方を備えさせる。



110
111
112
113
114
115
116
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 110

def build_svg(url)
  svg = RQRCode::QRCode.new(url).as_svg(use_path: true, module_size: MODULE_SIZE, viewbox: false)
  ensure_viewbox(svg)
rescue StandardError => e
  Common.log_debug("[qr] SVG 生成に失敗: #{url} - #{e.message}")
  nil
end

.ensure_viewbox(svg) ⇒ Object

width/height しか持たない SVG に同値の viewBox を補う(既にあればそのまま)。



119
120
121
122
123
124
125
126
127
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 119

def ensure_viewbox(svg)
  return svg if svg.nil? || svg.include?('viewBox=')

  width  = svg[/<svg[^>]*\bwidth="([\d.]+)"/, 1]
  height = svg[/<svg[^>]*\bheight="([\d.]+)"/, 1]
  return svg unless width && height

  svg.sub(/<svg\b/, %(<svg viewBox="0 0 #{width} #{height}"))
end

.render_one(url, out_dir) ⇒ Object

URL 1 件を SVG 化して タグ文字列を返す。生成できなければ nil。



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 94

def render_one(url, out_dir)
  name = "#{Digest::SHA1.hexdigest(url)[0, 12]}.svg"
  path = File.join(out_dir, name)

  unless File.exist?(path)
    svg = build_svg(url)
    return nil unless svg

    FileUtils.mkdir_p(out_dir)
    File.write(path, svg, encoding: 'utf-8')
  end

  %(<img class="vs-qr" src="images/#{REL_BASE}/#{name}" alt="#{CGI.escapeHTML(url)}">)
end

.replace_in_prose(content) ⇒ Object

コード(フェンス・インラインコード)を避けて各プローズ行を書き換える。 Masking.each_prose_line はフェンスだけを判定するため、インラインコード内の 例示(@qr:https://…)はここで退避してから戻す。



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 79

def replace_in_prose(content)
  lines = content.lines
  Masking.each_prose_line(content) do |line, lineno|
    spans = []
    masked = line.gsub(/`[^`\n]+`/) do
      spans << Regexp.last_match(0)
      "#{MASK}QR#{spans.size - 1}#{MASK}"
    end
    replaced = yield(masked, lineno)
    lines[lineno - 1] = replaced.gsub(/#{MASK}QR(\d+)#{MASK}/) { spans[Regexp.last_match(1).to_i] }
  end
  lines.join
end

.transform(content, source_filename:) ⇒ String

本文中の @qr:URL をすべて へ置換する。

Parameters:

  • content (String)

    処理対象の Markdown 本文

  • source_filename (String)

    警告に出す原稿ファイル名

Returns:

  • (String)

    置換後の本文(生成不能時は当該箇所を原文のまま残す)



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 63

def transform(content, source_filename:)
  return content unless content.include?('@qr:')
  return warn_gem_missing(content, source_filename) unless available?

  out_dir = File.join(Common::BUILD_HTML_DIR, 'images', REL_BASE)
  replace_in_prose(content) do |line, lineno|
    line.gsub(QR_PATTERN) do
      url = Regexp.last_match(1)
      render_one(url, out_dir) || warn_render_failed(source_filename, lineno, url)
    end
  end
end

.warn_gem_missing(content, source_filename) ⇒ Object

--- 警告(出現位置つき・修正案つき) ---



143
144
145
146
147
148
149
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 143

def warn_gem_missing(content, source_filename)
  Common.log_warn(
    "[qr] #{source_filename}: rqrcode が無いため @qr:URL を QR コードにできません(記法のまま出力します)",
    detail: '→ `bundle install`(または `gem install rqrcode`)で導入できます。'
  )
  content
end

.warn_render_failed(source_filename, lineno, url) ⇒ Object



151
152
153
154
155
156
157
# File 'lib/vivlio_starter/cli/pre_process/qr_transformer.rb', line 151

def warn_render_failed(source_filename, lineno, url)
  Common.log_warn(
    "[qr] #{source_filename}:#{lineno} - QR コードを生成できませんでした(記法のまま残します)",
    detail: "URL: #{url}\n→ URL が長すぎないか・不正な文字を含まないか確認してください。"
  )
  "@qr:#{url}"
end