Module: Asciidoctor::PDF::Rhrev::ChangeBars
- Included in:
- Converter
- Defined in:
- lib/asciidoctor/rhrev/change_bars.rb
Overview
Inks a vertical change bar in the page margin next to blocks marked with the current revision (revnumber 1.2 => attribute rhrev1-2). Enabled by the rhrev-change-bars document attribute.
Styling comes from the PDF theme by default (rhrev_change_bars category: color, width, offset, side), overridable per document via the rhrev-change-bars-color/-width/-offset/-side attributes.
Constant Summary collapse
- ARRANGED_BLOCK_CONTEXTS =
Contexts that render through this hook unconditionally in asciidoctor-pdf: examples and listings (original coverage), plus admonition, quote, verse, and sidebar, so widening this one check is the whole fix for their bar-inking. open is deliberately NOT here: asciidoctor-pdf's convert_open only calls arrange_block when the block has a title, an id, or the unbreakable option, a plain open block skips it and just traverses directly, so it needs its own manual bracket instead (see Converter#convert_open), the same shape as paragraphs and the three list types below. Tables and images never reach this hook with their own node either and are handled the same way.
[:example, :listing, :admonition, :quote, :verse, :sidebar].freeze
Instance Method Summary collapse
-
#arrange_block(node, &block) ⇒ Object
The extent yielded here is exact across page breaks.
-
#bracket_change_bar(node) ⇒ Object
Brackets a block conversion with a before/after page-cursor snapshot and inks the bar across whatever it spanned, for converters that don't route through arrange_block: paragraphs and the three list types (convert_paragraph, convert_ulist, convert_olist, convert_dlist).
- #change_bar?(node) ⇒ Boolean
-
#change_bar_settings ⇒ Object
Resolved lazily on first ink: the theme (and @media/@folio_placement) are not loaded yet when init_change_bars runs; upstream convert_document sets them up after rhrev's pre-super initialization.
-
#change_bar_x(pgnum, settings) ⇒ Object
The offset measures the gap between the text edge and the near edge of the bar.
-
#enter_change_bar_section(node) ⇒ Object
A section's bar covers only its own directly-owned content, not any nested child sections/chapters: as soon as a child section begins rendering, close out the bar of every still-open ancestor section right here, at the boundary between the parent's own content and its first child section.
- #exit_change_bar_section(node) ⇒ Object
- #init_change_bars(doc) ⇒ Object
-
#ink_change_bar(from, to) ⇒ Object
Paints the bar page by page; from/to are { page:, cursor: }.
- #ink_change_bar_for_extent(extent) ⇒ Object
- #ink_chapter_title(node, title, opts = {}) ⇒ Object
-
#ink_general_heading(node, title, opts = {}) ⇒ Object
The heading ink methods run after arrange_heading / start_new_chapter have settled the page, so this is the first accurate start position.
-
#ink_part_title(node, title, opts = {}) ⇒ Object
Upstream defines ink_part_title as an alias copy of ink_chapter_title, so it must be overridden separately.
- #record_change_bar_start(node) ⇒ Object
- #take_change_bar_start(node) ⇒ Object
Instance Method Details
#arrange_block(node, &block) ⇒ Object
The extent yielded here is exact across page breaks.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 142 def arrange_block node, &block if @change_bar_attr && !scratch? && (ARRANGED_BLOCK_CONTEXTS.include? node.context) && ( node) super node do |extent| if scratch? # measurement pass: upstream instance_execs this wrapper on the scratch # document, so re-bind the original block to it the same way instance_exec(&block) else block.call extent extent end end else super end end |
#bracket_change_bar(node) ⇒ Object
Brackets a block conversion with a before/after page-cursor snapshot and inks the bar across whatever it spanned, for converters that don't route through arrange_block: paragraphs and the three list types (convert_paragraph, convert_ulist, convert_olist, convert_dlist). Same shape as convert_table/convert_image, pulled out as a shared helper since none of the four need any special- casing the others don't.
167 168 169 170 171 172 173 174 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 167 def node if @change_bar_attr && !scratch? && ( node) = { page: page_number, cursor: cursor } end result = yield , { page: page_number, cursor: cursor } if result end |
#change_bar?(node) ⇒ Boolean
52 53 54 55 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 52 def node @change_bar_attr && node.respond_to?(:attributes) && node.attributes && (node.attributes.key? @change_bar_attr) end |
#change_bar_settings ⇒ Object
Resolved lazily on first ink: the theme (and @media/@folio_placement) are not loaded yet when init_change_bars runs; upstream convert_document sets them up after rhrev's pre-super initialization. Precedence: document attribute > theme key > built-in default.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 37 def @change_bar_settings ||= begin overrides = @change_bar_overrides || {} theme = @theme side = overrides[:side] || theme&. || (@media == 'prepress' ? 'outer' : 'right') { color: (overrides[:color] || theme&. || 'FF0000').to_s.delete_prefix('#').upcase, width: (overrides[:width] || theme&. || 2).to_f, offset: (overrides[:offset] || theme&. || 16).to_f, side: side.to_s, } end end |
#change_bar_x(pgnum, settings) ⇒ Object
The offset measures the gap between the text edge and the near edge of the bar. Sides recto/verso are determined by the physical page number (honoring pdf-folio-placement inversion), matching prepress physical folio placement.
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 91 def pgnum, settings case settings[:side] when 'right' on_right = true when 'outer', 'inner' recto = (page_side pgnum, @folio_placement && @folio_placement[:inverted]) == :recto on_right = settings[:side] == 'outer' ? recto : !recto end on_right ? bounds.right + settings[:offset] : bounds.left - settings[:offset] - settings[:width] end |
#enter_change_bar_section(node) ⇒ Object
A section's bar covers only its own directly-owned content, not any nested child sections/chapters: as soon as a child section begins rendering, close out the bar of every still-open ancestor section right here, at the boundary between the parent's own content and its first child section. A section carrying the recursive option keeps its bar open across child boundaries; convert_section closes it after the whole section, children included, has rendered.
72 73 74 75 76 77 78 79 80 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 72 def node return unless @change_bar_attr @change_bar_section_stack.each do |ancestor| next if ancestor.option? 'recursive' next unless (start_pos = ancestor) start_pos, { page: page_number, cursor: cursor } end @change_bar_section_stack.push node end |
#exit_change_bar_section(node) ⇒ Object
82 83 84 85 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 82 def node return unless @change_bar_attr @change_bar_section_stack.pop end |
#init_change_bars(doc) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 14 def doc @change_bar_attr = nil @change_bar_settings = nil return unless doc.attr? 'rhrev-change-bars' # Without a revnumber there is no "current" revision to match return unless (revnumber = doc.attr 'revnumber') @change_bar_attr = %(#{@revision_prefix}#{revnumber.to_s.tr '.', '-'}) # Capture attribute values only; holding the Document in an ivar here # would leak it into the scratch prototype, which must stay marshalable @change_bar_overrides = { color: (doc.attr 'rhrev-change-bars-color'), width: (doc.attr 'rhrev-change-bars-width'), offset: (doc.attr 'rhrev-change-bars-offset'), side: (doc.attr 'rhrev-change-bars-side'), } @change_bar_starts = {} @change_bar_section_stack = [] end |
#ink_change_bar(from, to) ⇒ Object
Paints the bar page by page; from/to are { page:, cursor: }. Called immediately after the block is rendered, so all spanned pages exist.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 104 def from, to return if scratch? || from.nil? || to.nil? return if to[:page] < from[:page] return if to[:page] == from[:page] && to[:cursor] >= from[:cursor] settings = float do (from[:page]..to[:page]).each do |pgnum| go_to_page pgnum unless page_number == pgnum top = pgnum == from[:page] ? from[:cursor] : bounds.top bottom = pgnum == to[:page] ? to[:cursor] : 0 next if (height = top - bottom) <= 0 bounding_box [( pgnum, settings), top], width: settings[:width], height: height do fill_bounds settings[:color] end end end end |
#ink_change_bar_for_extent(extent) ⇒ Object
122 123 124 125 126 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 122 def extent return unless extent ({ page: extent.from.page, cursor: extent.from.cursor }, { page: extent.to.page, cursor: extent.to.cursor }) end |
#ink_chapter_title(node, title, opts = {}) ⇒ Object
183 184 185 186 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 183 def ink_chapter_title node, title, opts = {} node if @change_bar_attr && !scratch? && ( node) super end |
#ink_general_heading(node, title, opts = {}) ⇒ Object
The heading ink methods run after arrange_heading / start_new_chapter have settled the page, so this is the first accurate start position.
178 179 180 181 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 178 def ink_general_heading node, title, opts = {} node if @change_bar_attr && !scratch? && ( node) super end |
#ink_part_title(node, title, opts = {}) ⇒ Object
Upstream defines ink_part_title as an alias copy of ink_chapter_title, so it must be overridden separately
190 191 192 193 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 190 def ink_part_title node, title, opts = {} node if @change_bar_attr && !scratch? && ( node) super end |
#record_change_bar_start(node) ⇒ Object
57 58 59 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 57 def node @change_bar_starts[node] = { page: page_number, cursor: cursor } end |
#take_change_bar_start(node) ⇒ Object
61 62 63 |
# File 'lib/asciidoctor/rhrev/change_bars.rb', line 61 def node @change_bar_starts&.delete node end |