Module: Vivlio::Starter::CLI::CreateCommands
- Defined in:
- lib/vivlio/starter/cli/create.rb
Overview
章ファイル・特殊ページ生成ロジック
Samovar CLI コマンドから呼び出される実行メソッド群。各メソッドは純粋な Hash オプションを受け取る。
Constant Summary collapse
- MAX_AUTO_CHAPTER =
98- LIGHT_PALETTE =
カバーテーマパレット定数
bundled テンプレートの CSS 変数値を差し替えるための対応表。キーは <style> ブロック内の CSS カスタムプロパティ名と一致させる。グラデーション stop-color は rsvg-convert の CSS 変数非対応のため{bg-from} / {bg-to} プレースホルダーとして別管理する。
{ # グラデーション(stop-color プレースホルダー) '{{bg-from}}' => '#f8f6f2', '{{bg-to}}' => '#f0ece4', # CSS 変数 '--vs-text-main' => '#1e3a60', '--vs-text-sub' => '#8090a8', '--vs-text-author' => '#2a4070', '--vs-text-label' => '#7080a0', '--vs-grid-stroke' => '#d0d0d0', '--vs-stroke-outer' => '#e0e0e0', '--vs-stroke-mid' => '#e8e8e8', '--vs-stroke-inner' => '#d0d0d0', '--vs-node-fill' => '#ffffff', '--vs-node-stroke' => '#1e3a60', '--vs-icon-color' => '#1e3a60' }.freeze
- DARK_PALETTE =
{ # グラデーション(stop-color プレースホルダー) '{{bg-from}}' => '#0e1a2e', '{{bg-to}}' => '#060d1a', # CSS 変数 '--vs-text-main' => '#f0e8d0', '--vs-text-sub' => '#a8b8d0', '--vs-text-author' => '#d0c0a0', '--vs-text-label' => '#7080a0', '--vs-grid-stroke' => '#1e3358', '--vs-stroke-outer' => '#1a3560', '--vs-stroke-mid' => '#1e4070', '--vs-stroke-inner' => '#2a5590', '--vs-node-fill' => '#0e2040', '--vs-node-stroke' => '#4a80c8', '--vs-icon-color' => '#5b9ef5' }.freeze
- COVER_SIZES =
定数
{ a4: { width_mm: 210, height_mm: 297 }, b5: { width_mm: 182, height_mm: 257 }, a5: { width_mm: 148, height_mm: 210 } }.freeze
- DPI =
350- MM_PER_INCH =
25.4- CROP_MARK_OFFSET_MM =
13.0- DEFAULT_DISCLAIMER =
<<~TXT.strip 本書は教育目的で作成された入門書であり、情報の提供のみを目的としています。内容の正確性には万全を期しておりますが、技術的な詳細については、専門的な文献もあわせてご参照ください。 本書の内容を参考にした結果生じた損害や、本書の内容を実行・運用・適用したことによって発生した問題について、著者・発行者および関係者は一切の責任を負いかねます。 TXT
- DEFAULT_TRADEMARK =
<<~TXT.strip 本書に登場するシステム名や製品名は、関係各社の商標または登録商標です。 本書では ™、®、© などのマークは省略しています。 TXT
Class Method Summary collapse
-
.add_crop_marks_overlay(pdf_path, trim_w_mm, trim_h_mm, bleed_mm, crop_offset_mm) ⇒ Object
トンボ線オーバーレイを生成し、カバーPDFに合成する crop offset 領域(bleed 外側)のみに描画し、カバー内部に食い込まない: - 角トンボ: trim境界位置の直線を bleed 外側に配置 - センタートンボ: 丸十字 ⊕ を crop offset 帯の中央に配置.
-
.apply_palette(svg, palette) ⇒ String
SVGにパレット(CSS変数値 + stop-colorプレースホルダー)を適用する.
-
.apply_text_placeholders_to_svg(user_svg_path, side, theme, covers_dir, _book_config_path) ⇒ String
ユーザー用意のSVGにテキストプレースホルダーのみ適用して出力SVGを生成する.
-
.apply_text_replacements(svg) ⇒ String
SVGにテキストプレースホルダーを適用する.
-
.apply_verbose(options) ⇒ Object
章番号管理ヘルパー ================================================================.
-
.bundled_template_path(name) ⇒ String
gem同梱テンプレートのパスを返す.
-
.check_image_resolution(image_path, theme) ⇒ Object
解像度チェック ================================================================.
-
.convert_png(input, output) ⇒ Object
PNGを変換(ImageMagick) 常に再生成する(book.yml / PNG 変更を確実に反映するため).
-
.convert_svg(input, output, page_size: :b5, crop_marks: false) ⇒ Object
SVGを変換する.
-
.convert_svg_to_pdf(input, output, w_mm, h_mm) ⇒ Object
SVG → PDF(rsvg-convert 優先、フォールバック: ImageMagick).
-
.convert_svg_to_pdf_with_crop_marks(input, output, trim_w_mm, trim_h_mm) ⇒ Object
SVG → PDF(トンボ・塗り足し付き入稿用) 1.
-
.convert_svg_to_raster(input, output, w_mm, h_mm) ⇒ Object
SVG → JPG/PNG(ImageMagick).
-
.create_image_directory(fname, _options = {}) ⇒ Object
章に対応する画像ディレクトリを生成する.
-
.create_markdown_file(fname, content) ⇒ Object
Markdown ファイルを contents/ に作成する.
-
.create_single_chapter(fname, entry) ⇒ void
単一の章ファイルと関連リソースを生成する.
-
.draw_center_crop_mark(pdf, cx, cy, half_h, half_v, radius) ⇒ Object
センタートンボ: ⊕(円+十字線).
-
.draw_corner_crop_mark(pdf, x, y, dx, dy, s, bl) ⇒ Object
角トンボ: 二重L字交差型.
-
.ensure_filename(name) ⇒ String?
章名を正規化し、ファイル名形式(XX-slug.md)に変換する.
- .ensure_names_present!(names) ⇒ Object
- .ensure_names_present?(names) ⇒ Boolean
-
.execute_colophon(options) ⇒ void
奥付ページを config/book.yml から生成する.
-
.execute_cover(options) ⇒ Object
表紙・裏表紙を生成する.
-
.execute_create(options, names) ⇒ void
章ファイルと画像ディレクトリを一括生成する.
-
.execute_legalpage(options) ⇒ void
免責事項・商標情報を含むリーガルページを生成する.
-
.execute_titlepage(options) ⇒ void
タイトルページ(扉)を config/book.yml から生成する.
-
.expand_css_custom_properties(svg_content) ⇒ String
CSS カスタムプロパティをインライン展開する.
-
.extract_css_variables(svg_content) ⇒ Hash{String => String}
<style> 内の :root { … } からカスタムプロパティを抽出する.
- .extract_number(basename) ⇒ Object
-
.extract_title_and_subtitle ⇒ Object
config/book.yml からタイトルとサブタイトルを取得する.
-
.fetch_config_value(section, key) ⇒ Object
config/book.yml から指定キーの値を取得する.
-
.generate_content_from_template(entry, title) ⇒ Object
テンプレートから章コンテンツを生成する.
-
.generate_cover_outputs_from_png(png_path, side, theme, targets, covers_dir) ⇒ Object
PNGから最終成果物(PDF / JPG)を生成する.
-
.generate_cover_outputs_from_svg(svg_path, side, theme, targets, covers_dir) ⇒ Object
SVGから最終成果物(PDF / JPG)を生成する.
-
.generate_title(fname) ⇒ Object
ファイル名から章タイトルを抽出する.
-
.kanji_year(num) ⇒ Object
西暦から和暦の漢数字表記を生成する.
-
.legal_texts ⇒ Object
config/book.yml から免責・商標文面を取得する.
- .next_available_number!(used_numbers) ⇒ Object
- .normalize_name_inputs(names, resolver) ⇒ Object
-
.normalize_slug(value) ⇒ Object
slug を chapter 名として利用できる形式へ正規化する.
- .numbered_basename?(basename) ⇒ Boolean
-
.process_cover_side(side, theme, targets, covers_dir, book_config_path) ⇒ Object
片面(front / back)のカバー処理を行う.
-
.render_bundled_svg(template_path, side, theme, covers_dir, _book_config_path) ⇒ String
gem同梱テンプレートにパレット+テキストを適用して出力SVGを生成する.
-
.resolve_cover_source(side, theme, covers_dir) ⇒ Hash
カバーのソースファイルを優先順位に従って解決する.
-
.resolve_cover_targets ⇒ Array<String>
カバー targets を解決する.
-
.resolve_cover_theme ⇒ String?
カバーテーマを解決する.
-
.resolve_css_variables(svg_content, variables, depth: 10) ⇒ String
var(–xxx) / var(–xxx, fallback) を再帰的に解決する.
-
.resolve_page_size ⇒ Symbol
book.yml の page.use からページサイズシンボルを解決する.
-
.safe_write(path, content) ⇒ Object
ファイルを安全に書き込む(親ディレクトリを自動作成).
- .strip_token_basename(token) ⇒ Object
-
.subtitle_style ⇒ Object
サブタイトルの装飾スタイルを取得する.
- .template_path_for(entry) ⇒ Object
- .used_numbers_pool(resolver) ⇒ Object
Class Method Details
.add_crop_marks_overlay(pdf_path, trim_w_mm, trim_h_mm, bleed_mm, crop_offset_mm) ⇒ Object
トンボ線オーバーレイを生成し、カバーPDFに合成するcrop offset 領域(bleed 外側)のみに描画し、カバー内部に食い込まない:
- 角トンボ: trim境界位置の直線を bleed 外側に配置
- センタートンボ: 丸十字 ⊕ を crop offset 帯の中央に配置
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 |
# File 'lib/vivlio/starter/cli/create.rb', line 629 def (pdf_path, trim_w_mm, trim_h_mm, bleed_mm, crop_offset_mm) require 'prawn' require 'combine_pdf' mm2pt = 72.0 / 25.4 margin_mm = bleed_mm + crop_offset_mm page_w_pt = (trim_w_mm + (2 * margin_mm)) * mm2pt page_h_pt = (trim_h_mm + (2 * margin_mm)) * mm2pt margin_pt = margin_mm * mm2pt bleed_pt = bleed_mm * mm2pt crop_off_pt = crop_offset_mm * mm2pt # 仕上がり線(trim)の座標(PDF座標: 左下原点) tx1 = margin_pt ty1 = margin_pt tx2 = margin_pt + (trim_w_mm * mm2pt) ty2 = margin_pt + (trim_h_mm * mm2pt) # bleed 境界の座標 line_w_pt = 0.24 circle_r_pt = 2.5 * mm2pt cross_arm_h_pt = 10.0 * mm2pt cross_arm_v_pt = 5.0 * mm2pt corner_len_pt = 10.0 * mm2pt = "#{pdf_path}.crop_marks.pdf" Prawn::Document.generate(, page_size: [page_w_pt, page_h_pt], margin: 0) do |pdf| pdf.stroke_color '000000' pdf.line_width line_w_pt s = corner_len_pt bl = bleed_pt draw_corner_crop_mark(pdf, tx1, ty2, -1, 1, s, bl) draw_corner_crop_mark(pdf, tx2, ty2, 1, 1, s, bl) draw_corner_crop_mark(pdf, tx1, ty1, -1, -1, s, bl) draw_corner_crop_mark(pdf, tx2, ty1, 1, -1, s, bl) cx = page_w_pt / 2.0 cy = page_h_pt / 2.0 mid_crop = crop_off_pt / 2.0 draw_center_crop_mark(pdf, cx, page_h_pt - mid_crop, cross_arm_h_pt, cross_arm_v_pt, circle_r_pt) draw_center_crop_mark(pdf, cx, mid_crop, cross_arm_h_pt, cross_arm_v_pt, circle_r_pt) draw_center_crop_mark(pdf, mid_crop, cy, cross_arm_v_pt, cross_arm_h_pt, circle_r_pt) draw_center_crop_mark(pdf, page_w_pt - mid_crop, cy, cross_arm_v_pt, cross_arm_h_pt, circle_r_pt) end base = CombinePDF.load(pdf_path) = CombinePDF.load() base.pages.first << .pages.first mm2pt_local = 72.0 / 25.4 trim_x1_pt = margin_pt trim_y1_pt = margin_pt trim_x2_pt = margin_pt + (trim_w_mm * mm2pt_local) trim_y2_pt = margin_pt + (trim_h_mm * mm2pt_local) bleed_x1_pt = trim_x1_pt - bleed_pt bleed_y1_pt = trim_y1_pt - bleed_pt bleed_x2_pt = trim_x2_pt + bleed_pt bleed_y2_pt = trim_y2_pt + bleed_pt page_dict = base.pages.first page_dict[:TrimBox] = [trim_x1_pt, trim_y1_pt, trim_x2_pt, trim_y2_pt] page_dict[:BleedBox] = [bleed_x1_pt, bleed_y1_pt, bleed_x2_pt, bleed_y2_pt] base.save(pdf_path) FileUtils.rm_f() rescue StandardError => e Common.log_warn("トンボ描画中にエラー: #{e.}") FileUtils.rm_f() if && File.exist?() end |
.apply_palette(svg, palette) ⇒ String
SVGにパレット(CSS変数値 + stop-colorプレースホルダー)を適用する
CSS変数の置換:
<style> ブロック内の "--vs-xxx: <現在値>;" を新しい値で上書きする。
正規表現: /--vs-xxx\s*:\s*[^;]+;/
stop-colorプレースホルダーの置換:
"{{bg-from}}" / "{{bg-to}}" をそのまま文字列置換する。
rsvg-convert は stop-color に CSS 変数を適用できないため、
テンプレート側でも {{}} 形式のプレースホルダーを使う。
419 420 421 422 423 424 425 426 427 428 429 430 |
# File 'lib/vivlio/starter/cli/create.rb', line 419 def apply_palette(svg, palette) palette.each do |key, value| svg = if key.start_with?('--') # CSS変数: "--vs-text-main: #1e3a60;" の値部分を差し替える svg.gsub(/#{Regexp.escape(key)}\s*:\s*[^;]+;/, "#{key}: #{value};") else # {{}} プレースホルダー: stop-color などの直接置換 svg.gsub(key, value) end end svg end |
.apply_text_placeholders_to_svg(user_svg_path, side, theme, covers_dir, _book_config_path) ⇒ String
ユーザー用意のSVGにテキストプレースホルダーのみ適用して出力SVGを生成する
パレットは適用しない(ユーザーが色を自由に設定しているため)。ただし CSS カスタムプロパティ(var(–xxx))は rsvg-convert が解釈できないためインライン展開してから保存する。生成したSVGは covers/<side>cover_<theme>_rendered.svg に保存する。
344 345 346 347 348 349 350 351 352 353 |
# File 'lib/vivlio/starter/cli/create.rb', line 344 def apply_text_placeholders_to_svg(user_svg_path, side, theme, covers_dir, _book_config_path) # 常に再生成する(book.yml / ユーザ SVG 変更を確実に反映するため) output_svg = File.join(covers_dir, "#{side}cover_#{theme}_rendered.svg") svg = File.read(user_svg_path, encoding: 'utf-8') svg = apply_text_replacements(svg) svg = (svg) safe_write(output_svg, svg) Common.log_info("#{side}表紙SVGを適用しました: #{output_svg}") output_svg end |
.apply_text_replacements(svg) ⇒ String
SVGにテキストプレースホルダーを適用する
book.yml から取得した書籍メタデータを {} プレースホルダーに埋め込む。SVGの構造やスタイルには一切手を加えない。
439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/vivlio/starter/cli/create.rb', line 439 def apply_text_replacements(svg) title, subtitle = extract_title_and_subtitle placeholders = { '{{title}}' => title, '{{subtitle}}' => subtitle, '{{author}}' => fetch_config_value('book', 'author'), '{{series}}' => fetch_config_value('book', 'series'), '{{release}}' => fetch_config_value('book', 'release') } placeholders.each { |ph, val| svg = svg.gsub(ph, val.to_s) } svg end |
.apply_verbose(options) ⇒ Object
章番号管理ヘルパー
1122 1123 1124 |
# File 'lib/vivlio/starter/cli/create.rb', line 1122 def apply_verbose() ENV['VERBOSE'] = '1' if [:verbose] end |
.bundled_template_path(name) ⇒ String
gem同梱テンプレートのパスを返す
covers/bundled/<name>.svg を参照する。プロジェクトルートで実行されることを前提とする。
300 301 302 |
# File 'lib/vivlio/starter/cli/create.rb', line 300 def bundled_template_path(name) File.join(Dir.pwd, 'covers', 'bundled', "#{name}.svg") end |
.check_image_resolution(image_path, theme) ⇒ Object
解像度チェック
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 |
# File 'lib/vivlio/starter/cli/create.rb', line 770 def check_image_resolution(image_path, theme) return unless File.exist?(image_path) && system('identify -version > /dev/null 2>&1') dpi_output = `identify -format '%x' #{image_path}`.strip unless dpi_output.match?(/\A\d+/) Common.log_warn("解像度情報を解析できません: #{dpi_output}") return end avg_dpi = dpi_output.scan(/\d+/).map(&:to_i).then { it.sum / it.size } case avg_dpi when ...300 Common.log_warn("カスタム画像 '#{theme}' の解像度が不足しています") Common.log_warn(" 現在: #{avg_dpi}dpi(推奨: 350dpi以上、最小: 300dpi以上)") Common.log_warn(' ビルドは続行しますが、印刷品質が低下する可能性があります') when 300...350 Common.log_info("カスタム画像 '#{theme}' の解像度: #{avg_dpi}dpi(推奨: 350dpi以上)") end rescue StandardError => e Common.log_warn("解像度チェック中にエラーが発生しました: #{e.}") end |
.convert_png(input, output) ⇒ Object
PNGを変換(ImageMagick)常に再生成する(book.yml / PNG 変更を確実に反映するため)
752 753 754 755 756 757 758 759 760 761 762 763 764 |
# File 'lib/vivlio/starter/cli/create.rb', line 752 def convert_png(input, output) convert_cmd = CoverCommands.imagemagick_convert_command unless convert_cmd Common.log_error('ImageMagick(magick/convert)が見つかりません') return end ext = File.extname(output).delete('.').downcase density = ext == 'pdf' ? '350' : '150' system(*convert_cmd, '-density', density, input, output) Common.log_info("カバーを生成しました: #{File.basename(output)}") end |
.convert_svg(input, output, page_size: :b5, crop_marks: false) ⇒ Object
SVGを変換する
PDF出力: rsvg-convert でページサイズを正確に一致させるJPG/PNG出力: ImageMagick でラスター変換
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
# File 'lib/vivlio/starter/cli/create.rb', line 522 def convert_svg(input, output, page_size: :b5, crop_marks: false) # 常に再生成する(book.yml / テンプレート変更を確実に反映するため) ext = File.extname(output).delete('.') size = COVER_SIZES.fetch(page_size, COVER_SIZES[:b5]) w_mm = size[:width_mm] h_mm = size[:height_mm] if ext == 'pdf' if crop_marks convert_svg_to_pdf_with_crop_marks(input, output, w_mm, h_mm) else convert_svg_to_pdf(input, output, w_mm, h_mm) end else convert_svg_to_raster(input, output, w_mm, h_mm) end Common.log_info("カバーを生成しました: #{File.basename(output)}") end |
.convert_svg_to_pdf(input, output, w_mm, h_mm) ⇒ Object
SVG → PDF(rsvg-convert 優先、フォールバック: ImageMagick)
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
# File 'lib/vivlio/starter/cli/create.rb', line 543 def convert_svg_to_pdf(input, output, w_mm, h_mm) if CoverCommands.find_executable('rsvg-convert') Common.run_svg_converter!( ['rsvg-convert', '-f', 'pdf', '--page-width', "#{w_mm}mm", '--page-height', "#{h_mm}mm", '-w', "#{w_mm}mm", '-h', "#{h_mm}mm", '-o', output, input], input_path: input, output_path: output, purpose: 'カバー PDF 変換' ) else convert_cmd = CoverCommands.imagemagick_convert_command unless convert_cmd Common.log_error('rsvg-convert も ImageMagick も見つかりません') return false end w_px = (w_mm / MM_PER_INCH * DPI).round h_px = (h_mm / MM_PER_INCH * DPI).round Common.run_svg_converter!( [*convert_cmd, '-density', DPI.to_s, input, '-resize', "#{w_px}x#{h_px}!", output], input_path: input, output_path: output, purpose: 'カバー PDF 変換 (ImageMagick)' ) end end |
.convert_svg_to_pdf_with_crop_marks(input, output, trim_w_mm, trim_h_mm) ⇒ Object
SVG → PDF(トンボ・塗り足し付き入稿用)
-
rsvg-convert で大ページ(trim + bleed×2 + crop_offset×2)にSVGを配置
-
Prawn でトンボ線のみのオーバーレイPDFを生成
-
CombinePDF で合成
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 |
# File 'lib/vivlio/starter/cli/create.rb', line 576 def convert_svg_to_pdf_with_crop_marks(input, output, trim_w_mm, trim_h_mm) bleed_mm = Build::NombreStamper.bleed_mm_from_config crop_offset_mm = CROP_MARK_OFFSET_MM margin_mm = bleed_mm + crop_offset_mm page_w_mm = trim_w_mm + (2 * margin_mm) page_h_mm = trim_h_mm + (2 * margin_mm) # SVGをbleedサイズで描画(背景色が塗り足し領域まで伸びる) svg_w_mm = trim_w_mm + (2 * bleed_mm) svg_h_mm = trim_h_mm + (2 * bleed_mm) unless CoverCommands.find_executable('rsvg-convert') Common.log_warn('rsvg-convert が見つかりません。トンボなしで生成します') convert_svg_to_pdf(input, output, trim_w_mm, trim_h_mm) return end # rsvg-convert は CSS var() を完全サポートしないため、変換前にインライン展開する svg_content = File.read(input, encoding: 'utf-8') = (svg_content) input_to_use = if == svg_content input else tmp = "#{input}.expanded.svg" File.write(tmp, , encoding: 'utf-8') tmp end success = Common.run_svg_converter!( ['rsvg-convert', '-f', 'pdf', '--page-width', "#{page_w_mm}mm", '--page-height', "#{page_h_mm}mm", '-w', "#{svg_w_mm}mm", '-h', "#{svg_h_mm}mm", '--left', "#{crop_offset_mm}mm", '--top', "#{crop_offset_mm}mm", '-o', output, input_to_use], input_path: input, output_path: output, purpose: 'カバー PDF(トンボ付き)変換' ) FileUtils.rm_f(input_to_use) if input_to_use != input return unless success (output, trim_w_mm, trim_h_mm, bleed_mm, crop_offset_mm) end |
.convert_svg_to_raster(input, output, w_mm, h_mm) ⇒ Object
SVG → JPG/PNG(ImageMagick)
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 |
# File 'lib/vivlio/starter/cli/create.rb', line 731 def convert_svg_to_raster(input, output, w_mm, h_mm) convert_cmd = CoverCommands.imagemagick_convert_command unless convert_cmd Common.log_error('ImageMagick(magick/convert)が見つかりません') return false end raster_dpi = 150 w_px = (w_mm / MM_PER_INCH * raster_dpi).round h_px = (h_mm / MM_PER_INCH * raster_dpi).round Common.run_svg_converter!( [*convert_cmd, '-density', raster_dpi.to_s, input, '-resize', "#{w_px}x#{h_px}!", '-quality', '90', output], input_path: input, output_path: output, purpose: 'カバー画像 (JPG/PNG) 変換' ) end |
.create_image_directory(fname, _options = {}) ⇒ Object
章に対応する画像ディレクトリを生成する
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
# File 'lib/vivlio/starter/cli/create.rb', line 1034 def create_image_directory(fname, = {}) basename = File.basename(fname, '.md') dir = File.join(Common::IMAGES_DIR, basename) if Dir.exist?(dir) Common.log_info("画像ディレクトリは既に存在します: #{dir}") return dir end FileUtils.mkdir_p(dir) Common.log_success("画像ディレクトリを作成しました: #{dir}") dir end |
.create_markdown_file(fname, content) ⇒ Object
Markdown ファイルを contents/ に作成する
1025 1026 1027 1028 1029 1030 1031 |
# File 'lib/vivlio/starter/cli/create.rb', line 1025 def create_markdown_file(fname, content) path = File.join(Common::CONTENTS_DIR, fname) raise "既に存在します: #{path}" if File.exist?(path) safe_write(path, content) path end |
.create_single_chapter(fname, entry) ⇒ void
This method returns an undefined value.
単一の章ファイルと関連リソースを生成する
953 954 955 956 957 958 959 960 961 962 963 |
# File 'lib/vivlio/starter/cli/create.rb', line 953 def create_single_chapter(fname, entry) title = generate_title(fname) content = generate_content_from_template(entry, title) path = create_markdown_file(fname, content) create_image_directory(fname, {}) basename = File.basename(fname, '.md') Build::CatalogUpdater.add_chapter(basename) Common.log_success("#{path} を作成しました") end |
.draw_center_crop_mark(pdf, cx, cy, half_h, half_v, radius) ⇒ Object
センタートンボ: ⊕(円+十字線)
711 712 713 714 715 |
# File 'lib/vivlio/starter/cli/create.rb', line 711 def draw_center_crop_mark(pdf, cx, cy, half_h, half_v, radius) pdf.stroke_line [cx - half_h, cy], [cx + half_h, cy] pdf.stroke_line [cx, cy - half_v], [cx, cy + half_v] pdf.stroke_circle [cx, cy], radius end |
.draw_corner_crop_mark(pdf, x, y, dx, dy, s, bl) ⇒ Object
角トンボ: 二重L字交差型
718 719 720 721 722 723 724 725 726 727 728 |
# File 'lib/vivlio/starter/cli/create.rb', line 718 def draw_corner_crop_mark(pdf, x, y, dx, dy, s, bl) pdf.move_to(x + (bl * dx), y) pdf.line_to(x + ((s + bl) * dx), y) pdf.move_to(x, y + (bl * dy)) pdf.line_to(x, y + ((s + bl) * dy)) pdf.move_to(x, y + (bl * dy)) pdf.line_to(x + (s * dx), y + (bl * dy)) pdf.move_to(x + (bl * dx), y) pdf.line_to(x + (bl * dx), y + (s * dy)) pdf.stroke end |
.ensure_filename(name) ⇒ String?
章名を正規化し、ファイル名形式(XX-slug.md)に変換する
969 970 971 972 973 974 975 976 977 978 979 980 |
# File 'lib/vivlio/starter/cli/create.rb', line 969 def ensure_filename(name) return nil if name.nil? n = name.to_s.strip n = File.basename(n) n = File.basename(n, '.md') return nil unless n =~ /\A\d+(?:-[\w.-]+)?\z/ "#{n}.md" rescue StandardError nil end |
.ensure_names_present!(names) ⇒ Object
1168 1169 1170 1171 1172 1173 |
# File 'lib/vivlio/starter/cli/create.rb', line 1168 def ensure_names_present!(names) return if ensure_names_present?(names) Common.log_error('使い方: vs create NAME [NAME ...]') exit 1 end |
.ensure_names_present?(names) ⇒ Boolean
1126 1127 1128 |
# File 'lib/vivlio/starter/cli/create.rb', line 1126 def ensure_names_present?(names) !names.nil? && !names.empty? end |
.execute_colophon(options) ⇒ void
This method returns an undefined value.
奥付ページを config/book.yml から生成する
生成ファイル: .cache/vs/_colophon.md
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 |
# File 'lib/vivlio/starter/cli/create.rb', line 867 def execute_colophon() apply_verbose() title, subtitle = extract_title_and_subtitle = fetch_config_value('book', 'author') publisher = fetch_config_value('book', 'publisher') publisher = fetch_config_value('book', 'publisher_name') if publisher.empty? contact = fetch_config_value('book', 'contact') release = fetch_config_value('book', 'release') subtitle_class = "subtitle subtitle--#{subtitle_style}" current_wareki = "令和#{kanji_year(Time.now.year - 2018)}年" content = <<~MD <h1 class="book-title">#{title}</h1> #{%(<p class="#{subtitle_class}">#{subtitle}</p>) unless subtitle.empty?} #{%(<p class="publication-info">#{release}</p>) unless release.empty?} <dl class="info-list"> #{%(<dt>著者</dt>\n <dd>#{}</dd>) unless .empty?} #{%(<dt>発行者</dt>\n <dd>#{publisher}</dd>) unless publisher.empty?} #{%(<dt>連絡先</dt>\n <dd>#{contact}</dd>) unless contact.empty?} </dl> <p class="copyright"> <small> © #{current_wareki} #{.empty? ? '著者' : } All rights reserved. </small> </p> <p class="powered-by"> <small> (powered by Vivlio Starter) </small> </p> MD # 常に上書き再生成する(book.yml 変更を確実に反映するため) path = File.join(Common::CACHE_DIR, '_colophon.md') safe_write(path, content) end |
.execute_cover(options) ⇒ Object
表紙・裏表紙を生成する
book.yml の cover: <theme> 設定を読み取り、以下の優先順位でソースを決定してから PDF / JPG を生成する:
1. covers/<side>cover_<theme>.png ユーザー用意のPNG
2. covers/<side>cover_<theme>.svg ユーザー用意のSVG
3. covers/bundled/<side>cover.svg gem同梱テンプレート(置換後)
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/vivlio/starter/cli/create.rb', line 179 def execute_cover() apply_verbose() theme = resolve_cover_theme unless theme Common.log_error('output.cover 設定が見つかりません') return false end targets = resolve_cover_targets Common.log_action("カバーを生成しています(テーマ: #{theme}, targets: #{targets.join(', ')})…") covers_dir = File.join(Dir.pwd, 'covers') book_config_path = File.join(Dir.pwd, 'config', 'book.yml') FileUtils.mkdir_p(covers_dir) %w[front back].each do |side| process_cover_side(side, theme, targets, covers_dir, book_config_path) end end |
.execute_create(options, names) ⇒ void
This method returns an undefined value.
章ファイルと画像ディレクトリを一括生成する
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/vivlio/starter/cli/create.rb', line 123 def execute_create(, names) apply_verbose() ensure_names_present!(names) resolver = TokenResolver::Resolver.new normalized_names = normalize_name_inputs(names, resolver) entries = resolver.resolve(normalized_names) # 1. 不正な形式をチェック invalid_entries = entries.reject(&:valid?) if invalid_entries.any? Common.log_error("エラー: 不正な形式が含まれています: #{invalid_entries.map(&:slug).join(', ')}") exit 1 end # 2. カタログとの重複をチェック duplicate_entries = entries.select(&:in_catalog?) if duplicate_entries.any? Common.log_error('エラー: 以下の章は既にカタログに存在します:') duplicate_entries.each { |e| Common.log_error(" - #{e.basename} (#{e.label})") } exit 1 end # 3. すべてクリアしたら、一括で作成 errors = false entries.each do |entry| fname = ensure_filename(entry.basename) unless fname Common.log_error("エラー: 無効なファイル名です: #{entry.basename}") errors = true next end create_single_chapter(fname, entry) rescue StandardError => e errors = true Common.log_error("作成に失敗しました: #{fname} (#{e.class}: #{e.})") end exit 1 if errors end |
.execute_legalpage(options) ⇒ void
This method returns an undefined value.
免責事項・商標情報を含むリーガルページを生成する
生成ファイル: .cache/vs/_legalpage.md
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 |
# File 'lib/vivlio/starter/cli/create.rb', line 920 def execute_legalpage() apply_verbose() FileUtils.mkdir_p(Common::CACHE_DIR) # 常に上書き再生成する(book.yml 変更を確実に反映するため) target = File.join(Common::CACHE_DIR, '_legalpage.md') disclaimer, trademark = legal_texts body = <<~MD <h1 style="display: none;">本書について</h1> <div class="disclaimer"> <h2>■免責</h2> #{disclaimer.split(/\r?\n/).map { |line| " <p>#{line}</p>" }.join("\n")} </div> <div class="trademark"> <h2>■商標</h2> #{trademark.split(/\r?\n/).map { |line| " <p>#{line}</p>" }.join("\n")} </div> MD safe_write(target, body) Common.log_success("生成しました: #{target}") end |
.execute_titlepage(options) ⇒ void
This method returns an undefined value.
タイトルページ(扉)を config/book.yml から生成する
生成ファイル: .cache/vs/_titlepage.md
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 |
# File 'lib/vivlio/starter/cli/create.rb', line 830 def execute_titlepage() apply_verbose() title, subtitle = extract_title_and_subtitle = fetch_config_value('book', 'author') series = fetch_config_value('book', 'series') release = fetch_config_value('book', 'release') subtitle_class = "subtitle subtitle--#{subtitle_style}" content = <<~MD <h1 class="book-title">#{title}</h1> #{%(<p class="#{subtitle_class}">#{subtitle}</p>) unless subtitle.empty?} #{%(<p class="author"><span>[著]</span> #{}</p>) unless .empty?} #{%(<div class="publication-info">) unless series.empty? && release.empty?} #{%( <p class="series">#{series}</p>) unless series.empty?} #{%( <p class="release-info">#{release}</p>) unless release.empty?} #{%(</div>) unless series.empty? && release.empty?} MD # 常に上書き再生成する(book.yml 変更を確実に反映するため) path = File.join(Common::CACHE_DIR, '_titlepage.md') safe_write(path, content) end |
.expand_css_custom_properties(svg_content) ⇒ String
CSS カスタムプロパティをインライン展開する
rsvg-convert は CSS var() を完全サポートしていないため、:root ブロックで定義された変数を実際の値に展開してから渡す。
362 363 364 365 366 367 |
# File 'lib/vivlio/starter/cli/create.rb', line 362 def (svg_content) variables = extract_css_variables(svg_content) return svg_content if variables.empty? resolve_css_variables(svg_content, variables) end |
.extract_css_variables(svg_content) ⇒ Hash{String => String}
<style> 内の :root { … } からカスタムプロパティを抽出する
373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/vivlio/starter/cli/create.rb', line 373 def extract_css_variables(svg_content) variables = {} style_blocks = svg_content.scan(%r{<style[^>]*>(.*?)</style>}m).flatten style_blocks.each do |block| root_blocks = block.scan(/:root\s*\{([^}]*)\}/m).flatten root_blocks.each do |root_block| root_block.scan(/(--[\w-]+)\s*:\s*([^;]+);/) do |name, value| variables[name.strip] = value.strip end end end variables end |
.extract_number(basename) ⇒ Object
1162 1163 1164 1165 1166 |
# File 'lib/vivlio/starter/cli/create.rb', line 1162 def extract_number(basename) return unless basename =~ /\A(\d+)/ format('%02d', Regexp.last_match(1).to_i) end |
.extract_title_and_subtitle ⇒ Object
config/book.yml からタイトルとサブタイトルを取得する
1059 1060 1061 1062 1063 1064 |
# File 'lib/vivlio/starter/cli/create.rb', line 1059 def extract_title_and_subtitle book = Common::CONFIG.fetch('book', {}) title = (book['main_title'] || book['title'] || '').to_s subtitle = (book['subtitle'] || '').to_s [title, subtitle] end |
.fetch_config_value(section, key) ⇒ Object
config/book.yml から指定キーの値を取得する
1073 1074 1075 1076 |
# File 'lib/vivlio/starter/cli/create.rb', line 1073 def fetch_config_value(section, key) value = Common::CONFIG.dig(section, key) value ? value.to_s : '' end |
.generate_content_from_template(entry, title) ⇒ Object
テンプレートから章コンテンツを生成する
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 |
# File 'lib/vivlio/starter/cli/create.rb', line 1000 def generate_content_from_template(entry, title) tpl = template_path_for(entry) if tpl && File.exist?(tpl) File.read(tpl, encoding: 'utf-8').gsub('{{TITLE}}', title.to_s) else <<~MD # #{title} <!-- 章テンプレートが見つからなかったため、デフォルトの骨子を生成しました --> ここに#{title}の内容を記述してください。 MD end end |
.generate_cover_outputs_from_png(png_path, side, theme, targets, covers_dir) ⇒ Object
PNGから最終成果物(PDF / JPG)を生成する
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/vivlio/starter/cli/create.rb', line 490 def generate_cover_outputs_from_png(png_path, side, theme, targets, covers_dir) page_size = resolve_page_size if targets.include?('pdf') pdf_path = File.join(covers_dir, "#{side}cover_#{theme}_#{page_size}_rgb.pdf") convert_png(png_path, pdf_path) end if targets.include?('print_pdf') pdf_path = File.join(covers_dir, "#{side}cover_#{theme}_#{page_size}_cmyk.pdf") convert_png(png_path, pdf_path) end return unless targets.include?('epub') && side == 'front' jpg_path = File.join(covers_dir, "cover_#{theme}.jpg") convert_png(png_path, jpg_path) end |
.generate_cover_outputs_from_svg(svg_path, side, theme, targets, covers_dir) ⇒ Object
SVGから最終成果物(PDF / JPG)を生成する
targets の内容に応じて以下を生成する:
- 'pdf' → <side>cover_<theme>_<size>_rgb.pdf
- 'print_pdf' → <side>cover_<theme>_<size>_cmyk.pdf (トンボ付き)
- 'epub' → cover_<theme>.jpg (front のみ)
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/vivlio/starter/cli/create.rb', line 464 def generate_cover_outputs_from_svg(svg_path, side, theme, targets, covers_dir) page_size = resolve_page_size if targets.include?('pdf') pdf_path = File.join(covers_dir, "#{side}cover_#{theme}_#{page_size}_rgb.pdf") convert_svg(svg_path, pdf_path, page_size: page_size) end if targets.include?('print_pdf') pdf_path = File.join(covers_dir, "#{side}cover_#{theme}_#{page_size}_cmyk.pdf") convert_svg(svg_path, pdf_path, page_size: page_size, crop_marks: true) end return unless targets.include?('epub') && side == 'front' jpg_path = File.join(covers_dir, "cover_#{theme}.jpg") convert_svg(svg_path, jpg_path, page_size: page_size) end |
.generate_title(fname) ⇒ Object
ファイル名から章タイトルを抽出する
983 984 985 986 |
# File 'lib/vivlio/starter/cli/create.rb', line 983 def generate_title(fname) basename = File.basename(fname.to_s, '.md') basename.sub(/\A\d+-/, '') end |
.kanji_year(num) ⇒ Object
西暦から和暦の漢数字表記を生成する
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 |
# File 'lib/vivlio/starter/cli/create.rb', line 1079 def kanji_year(num) km = %w[〇 一 二 三 四 五 六 七 八 九] return '〇' if num <= 0 return km[num] if num < 10 return '十' if num == 10 tens = num / 10 ones = num % 10 result = '' result += "#{km[tens] unless tens == 1}十" result += km[ones] unless ones.zero? result end |
.legal_texts ⇒ Object
config/book.yml から免責・商標文面を取得する
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 |
# File 'lib/vivlio/starter/cli/create.rb', line 1094 def legal_texts legal = Common::CONFIG.fetch('legal', {}) disclaimer = (legal['disclaimer'] || '').strip trademark = (legal['trademark'] || '').strip if disclaimer.empty? && trademark.empty? Common.log_warn('config/book.yml の legal.disclaimer / legal.trademark が未設定です。テンプレート文面で生成します。') disclaimer = DEFAULT_DISCLAIMER trademark = DEFAULT_TRADEMARK end [disclaimer, trademark] end |
.next_available_number!(used_numbers) ⇒ Object
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 |
# File 'lib/vivlio/starter/cli/create.rb', line 1183 def next_available_number!(used_numbers) (1..MAX_AUTO_CHAPTER).each do |candidate| number = format('%02d', candidate) next if used_numbers.include?(number) used_numbers << number return number end raise '01-98 までの章番号がすべて使用済みです' end |
.normalize_name_inputs(names, resolver) ⇒ Object
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 |
# File 'lib/vivlio/starter/cli/create.rb', line 1130 def normalize_name_inputs(names, resolver) used_numbers = used_numbers_pool(resolver) Array(names).map do |raw| token = raw.to_s.strip basename = strip_token_basename(token) if numbered_basename?(basename) number = extract_number(basename) used_numbers << number if number && !used_numbers.include?(number) token else slug = normalize_slug(basename) number = next_available_number!(used_numbers) generated = "#{number}-#{slug}" Common.log_info("[create] #{basename} -> #{generated}") generated end end end |
.normalize_slug(value) ⇒ Object
slug を chapter 名として利用できる形式へ正規化する
989 990 991 992 993 994 995 996 997 |
# File 'lib/vivlio/starter/cli/create.rb', line 989 def normalize_slug(value) slug = value.to_s.downcase .tr(' ', '-') .gsub(/[^a-z0-9-]+/, '-') .gsub(/-+/, '-') .gsub(/\A-+|-+\z/, '') slug = 'chapter' if slug.empty? slug end |
.numbered_basename?(basename) ⇒ Boolean
1158 1159 1160 |
# File 'lib/vivlio/starter/cli/create.rb', line 1158 def numbered_basename?(basename) basename.match?(/\A\d+/) end |
.process_cover_side(side, theme, targets, covers_dir, book_config_path) ⇒ Object
片面(front / back)のカバー処理を行う
ソース解決 → SVG生成(必要な場合)→ PDF/JPG変換 の順に処理する。
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/vivlio/starter/cli/create.rb', line 233 def process_cover_side(side, theme, targets, covers_dir, book_config_path) source = resolve_cover_source(side, theme, covers_dir) case source[:type] when :png # PNG がそのままソースになる場合は解像度チェックのみ check_image_resolution(source[:path], theme) generate_cover_outputs_from_png(source[:path], side, theme, targets, covers_dir) when :user_svg # ユーザー用意のSVG: テキスト置換のみ行い、そのまま変換 svg_path = apply_text_placeholders_to_svg(source[:path], side, theme, covers_dir, book_config_path) generate_cover_outputs_from_svg(svg_path, side, theme, targets, covers_dir) when :bundled_svg # gem同梱テンプレート: パレット置換+テキスト置換してから変換 svg_path = render_bundled_svg(source[:path], side, theme, covers_dir, book_config_path) generate_cover_outputs_from_svg(svg_path, side, theme, targets, covers_dir) end end |
.render_bundled_svg(template_path, side, theme, covers_dir, _book_config_path) ⇒ String
gem同梱テンプレートにパレット+テキストを適用して出力SVGを生成する
生成したSVGは covers/<side>cover_<theme>.svg に保存する。book.yml またはテンプレートが更新されている場合のみ再生成する。
319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/vivlio/starter/cli/create.rb', line 319 def render_bundled_svg(template_path, side, theme, covers_dir, _book_config_path) # 常に再生成する(book.yml / テンプレート変更を確実に反映するため) output_svg = File.join(covers_dir, "#{side}cover_#{theme}.svg") palette = theme == 'dark' ? DARK_PALETTE : LIGHT_PALETTE svg = File.read(template_path, encoding: 'utf-8') svg = apply_palette(svg, palette) svg = apply_text_replacements(svg) safe_write(output_svg, svg) Common.log_info("#{side}表紙SVGを生成しました: #{output_svg}") output_svg end |
.resolve_cover_source(side, theme, covers_dir) ⇒ Hash
カバーのソースファイルを優先順位に従って解決する
優先順位:
1. covers/<side>cover_<theme>.png (ユーザー用意のPNG)
2. covers/<side>cover_<theme>.svg (ユーザー用意のSVG)
3. covers/bundled/<side>cover.svg (gem同梱テンプレート)
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/vivlio/starter/cli/create.rb', line 267 def resolve_cover_source(side, theme, covers_dir) basename = "#{side}cover_#{theme}" # 1. covers/<side>cover_<theme>.png png_path = File.join(covers_dir, "#{basename}.png") return { type: :png, path: png_path } if File.exist?(png_path) # 2. covers/<side>cover_<theme>.svg user_svg_path = File.join(covers_dir, "#{basename}.svg") return { type: :user_svg, path: user_svg_path } if File.exist?(user_svg_path) # 3. covers/bundled/<side>cover.svg bundled_path = bundled_template_path("#{side}cover") return { type: :bundled_svg, path: bundled_path } if File.exist?(bundled_path) # ソースが一切見つからない場合 Common.log_error(<<~MSG) #{side}cover のソースが見つかりません。 確認先: #{png_path} #{user_svg_path} #{bundled_path} MSG { type: :missing, path: nil } end |
.resolve_cover_targets ⇒ Array<String>
カバー targets を解決する
215 216 217 218 219 220 221 222 |
# File 'lib/vivlio/starter/cli/create.rb', line 215 def resolve_cover_targets raw_targets = Common::CONFIG.dig(:output, :targets) targets = Build::PdfMerger.extract_targets(raw_targets) if raw_targets targets = ['pdf'] if targets.nil? || targets.empty? targets rescue StandardError ['pdf'] end |
.resolve_cover_theme ⇒ String?
カバーテーマを解決する
204 205 206 207 208 209 210 |
# File 'lib/vivlio/starter/cli/create.rb', line 204 def resolve_cover_theme theme = Common.cover_theme return nil unless theme return nil if theme.strip.empty? theme end |
.resolve_css_variables(svg_content, variables, depth: 10) ⇒ String
var(–xxx) / var(–xxx, fallback) を再帰的に解決する
393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/vivlio/starter/cli/create.rb', line 393 def resolve_css_variables(svg_content, variables, depth: 10) return svg_content if depth.zero? resolved = svg_content.gsub(/var\((--[\w-]+)(?:\s*,\s*([^)]*))?\)/) do var_name = ::Regexp.last_match(1) fallback = ::Regexp.last_match(2)&.strip variables.fetch(var_name, fallback || 'unset') end resolved.include?('var(') ? resolve_css_variables(resolved, variables, depth: depth - 1) : resolved end |
.resolve_page_size ⇒ Symbol
book.yml の page.use からページサイズシンボルを解決する
799 800 801 802 |
# File 'lib/vivlio/starter/cli/create.rb', line 799 def resolve_page_size page_use = Common::CONFIG.dig(:page, :use) || 'b5_standard' CoverCommands.detect_page_size(page_use) end |
.safe_write(path, content) ⇒ Object
ファイルを安全に書き込む(親ディレクトリを自動作成)
1049 1050 1051 1052 |
# File 'lib/vivlio/starter/cli/create.rb', line 1049 def safe_write(path, content) FileUtils.mkdir_p(File.dirname(path)) File.write(path, content, encoding: 'utf-8') end |
.strip_token_basename(token) ⇒ Object
1151 1152 1153 1154 1155 1156 |
# File 'lib/vivlio/starter/cli/create.rb', line 1151 def strip_token_basename(token) base = File.basename(token.to_s.strip) base.sub(/\.(md|markdown)\z/i, '') rescue StandardError token.to_s end |
.subtitle_style ⇒ Object
サブタイトルの装飾スタイルを取得する
1067 1068 1069 1070 |
# File 'lib/vivlio/starter/cli/create.rb', line 1067 def subtitle_style style = fetch_config_value('book', 'subtitle_style').downcase %w[wave bar none].include?(style) ? style : 'wave' end |
.template_path_for(entry) ⇒ Object
1015 1016 1017 1018 1019 1020 1021 1022 |
# File 'lib/vivlio/starter/cli/create.rb', line 1015 def template_path_for(entry) case entry&.kind when :preface then Common.preface_template_path when :appendix then Common.appendix_template_path when :postface then Common.postface_template_path else Common.chapter_template_path end end |
.used_numbers_pool(resolver) ⇒ Object
1175 1176 1177 1178 1179 1180 1181 |
# File 'lib/vivlio/starter/cli/create.rb', line 1175 def used_numbers_pool(resolver) catalog_numbers = resolver.resolve([]).map(&:number).compact markdown_numbers = Dir.glob(File.join(Common::CONTENTS_DIR, '*.md')).filter_map do |path| File.basename(path, '.md')[/\A(\d{2})/, 1] end (catalog_numbers + markdown_numbers).uniq end |