Module: VivlioStarter::CLI::PostProcessCommands::PageBreakNormalizer
- Defined in:
- lib/vivlio_starter/cli/post_process/page_break_normalizer.rb
Overview
二重改ページを正規化するモジュール。
Constant Summary collapse
- BREAK_MARKERS =
改ページを引き起こす著者由来のマーカー要素。
'hr.pagebreak, div.vs-break-page, div.vs-break-recto, div.vs-break-verso'- MERGED_CLASS =
recto/verso 指定へ合流した h2 に付ける印(CSS 側で改ページを打ち消す)。
'vs-break-merged'- WRAPPER_ELEMENTS =
走査中に透過するラッパー要素(この中へ降りて最初の内容要素を探す)。
%w[section article div].freeze
Class Method Summary collapse
- .add_class(element, name) ⇒ Object
-
.breaking_h2_scope(doc) ⇒ Object
CSS で改ページする h2 の範囲を返す。正規化は「h2 が改ページする」ことが 前提なので、この範囲の外にあるマーカーは著者の意図どおり残す。.
-
.descend_to_content(element) ⇒ Object
ラッパー(section / article / div)なら、その中の最初の内容要素まで降りる。 空のラッパーはそれ自体を内容要素として返す(h2 ではないので正規化されない)。.
-
.first_content_child(node) ⇒ Object
空白テキスト・コメントを飛ばして最初の子要素を返す。.
-
.first_section_of_chapter?(h2) ⇒ Boolean
章(section.level1)の最初の節かどうか。章扉保護の CSS セレクタ
section.level2:first-of-typeと同じ判定を DOM 側で再現する。. -
.next_content_element(node) ⇒ Object
文書順で次に現れる「内容要素」を返す。 次の兄弟が無ければ親を遡り、見つけた要素がラッパーならその中へ降りる。.
-
.next_element_sibling(node) ⇒ Object
空白テキスト・コメントを飛ばして次の兄弟要素を返す。.
-
.normalize!(html_file) ⇒ Integer
二重改ページを正規化してファイルへ書き戻す。.
-
.normalize_marker(marker, scope = :all) ⇒ Boolean
マーカー 1 つを見て、直後が h2 なら正規化する。.
- .recto_or_verso?(marker) ⇒ Boolean
-
.section_pagebreak_enabled? ⇒ Boolean
節(h2)で改ページする設定かどうか。BookSettingsCss と同じ判定 (明示的に false と書かれたときだけ無効)を用いる。.
Class Method Details
.add_class(element, name) ⇒ Object
162 163 164 165 166 167 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 162 def add_class(element, name) classes = element['class'].to_s.split return if classes.include?(name) element['class'] = (classes + [name]).join(' ') end |
.breaking_h2_scope(doc) ⇒ Object
CSS で改ページする h2 の範囲を返す。正規化は「h2 が改ページする」ことが 前提なので、この範囲の外にあるマーカーは著者の意図どおり残す。
:all … 既定(section_pagebreak: true)。全 h2 が改ページする
:first_section_only … false かつ image スタイル。章扉を全面の扉絵で成立させる
ため章の最初の節だけ改ページが残る
(BookSettingsCss#chapter_frontispiece_guard_rule と対)
:none … false かつ simple スタイル。どの h2 も改ページしない
image スタイルの判定は config ではなく body クラスで行う——付録は theme.style: image でも常に vs-header-simple なので、CSS が実際に見るものと 同じ印を見るのが唯一ずれない方法(BodyClassInjector#header_mode_class)。
78 79 80 81 82 83 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 78 def breaking_h2_scope(doc) return :all if section_pagebreak_enabled? body_classes = doc.at_css('body')&.[]('class').to_s.split body_classes.include?('vs-header-image') ? :first_section_only : :none end |
.descend_to_content(element) ⇒ Object
ラッパー(section / article / div)なら、その中の最初の内容要素まで降りる。 空のラッパーはそれ自体を内容要素として返す(h2 ではないので正規化されない)。
140 141 142 143 144 145 146 147 148 149 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 140 def descend_to_content(element) node = element while WRAPPER_ELEMENTS.include?(node.name) child = first_content_child(node) return node unless child node = child end node end |
.first_content_child(node) ⇒ Object
空白テキスト・コメントを飛ばして最初の子要素を返す。
152 153 154 155 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 152 def first_content_child(node) child = node.children.find { it.element? || (it.text? && !it.text.strip.empty?) } child&.element? ? child : nil end |
.first_section_of_chapter?(h2) ⇒ Boolean
章(section.level1)の最初の節かどうか。章扉保護の CSS セレクタ
section.level2:first-of-type と同じ判定を DOM 側で再現する。
87 88 89 90 91 92 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 87 def first_section_of_chapter?(h2) level2 = h2.ancestors('section.level2').first return false unless level2 level2.parent.element_children.find { it.name == 'section' }.equal?(level2) end |
.next_content_element(node) ⇒ Object
文書順で次に現れる「内容要素」を返す。 次の兄弟が無ければ親を遡り、見つけた要素がラッパーならその中へ降りる。
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 114 def next_content_element(node) current = node while current sibling = next_element_sibling(current) return descend_to_content(sibling) if sibling current = current.parent return nil unless current&.element? end nil end |
.next_element_sibling(node) ⇒ Object
空白テキスト・コメントを飛ばして次の兄弟要素を返す。
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 127 def next_element_sibling(node) sibling = node.next_sibling while sibling return sibling if sibling.element? return nil if sibling.text? && !sibling.text.strip.empty? sibling = sibling.next_sibling end nil end |
.normalize!(html_file) ⇒ Integer
二重改ページを正規化してファイルへ書き戻す。
53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 53 def normalize!(html_file) doc = HtmlParser.parse_html_document(File.read(html_file, encoding: 'utf-8')) scope = breaking_h2_scope(doc) return 0 if scope == :none count = doc.css(BREAK_MARKERS).count { normalize_marker(it, scope) } return 0 if count.zero? HtmlParser.save_html_document(html_file, doc) Common.log_info("#{html_file}: 冗長な改ページを #{count} 件正規化しました") count end |
.normalize_marker(marker, scope = :all) ⇒ Boolean
マーカー 1 つを見て、直後が h2 なら正規化する。
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 97 def normalize_marker(marker, scope = :all) following = next_content_element(marker) return false unless following&.name == 'h2' return false if scope == :first_section_only && !first_section_of_chapter?(following) if recto_or_verso?(marker) # recto/verso 指定が勝つ。マーカーは残し、h2 側の改ページだけを無効化する。 add_class(following, MERGED_CLASS) else # 単純改ページは h2 自身の改ページと重複するので、マーカーごと落とす。 marker.remove end true end |
.recto_or_verso?(marker) ⇒ Boolean
157 158 159 160 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 157 def recto_or_verso?(marker) classes = marker['class'].to_s.split classes.include?('vs-break-recto') || classes.include?('vs-break-verso') end |
.section_pagebreak_enabled? ⇒ Boolean
節(h2)で改ページする設定かどうか。BookSettingsCss と同じ判定 (明示的に false と書かれたときだけ無効)を用いる。
171 172 173 174 175 176 177 178 |
# File 'lib/vivlio_starter/cli/post_process/page_break_normalizer.rb', line 171 def section_pagebreak_enabled? return true unless Common.configured? case Common::CONFIG.page.section_pagebreak.to_s.strip.downcase in 'false' | 'no' | 'off' | '0' then false else true end end |