Class: Asciidoctor::PDF::Rhrev::Converter
Constant Summary
collapse
- ROLLUP_CONTEXTS =
Contexts that never get a revision-history-table row of their own:
a marked one rolls up as a bullet into its nearest enclosing
section's entry instead (creating that entry if the section has
none yet for the matching revision), rather than needing its own
id, its own destination, or a generated label. See
rollup_child_revision_entries.
[:paragraph, :ulist, :olist, :dlist, :admonition, :open, :quote, :verse, :sidebar].freeze
Constants included
from ChangeBars
Asciidoctor::PDF::Rhrev::ChangeBars::ARRANGED_BLOCK_CONTEXTS
Instance Method Summary
collapse
-
#allocate_pagerhref_table_extent(node) ⇒ Object
-
#append_child_change_to_all(revision, change_text) ⇒ Object
Appends onto the document's existing -all change text for this revision, or sets it fresh if none exists yet.
-
#append_child_change_to_entry(revision, anchor_node, change_text) ⇒ Object
Finds anchor_node's existing entry for this revision and appends another bullet onto its :change text, or creates the entry if anchor_node has none yet.
-
#catalog_block_anchor(node) ⇒ Object
-
#collect_document_level_entries(doc) ⇒ Object
-
#collect_revision_entries(node) ⇒ Object
-
#convert_dlist(node) ⇒ Object
-
#convert_document(node) ⇒ Object
-
#convert_example(node) ⇒ Object
-
#convert_floating_title(node) ⇒ Object
-
#convert_image(node) ⇒ Object
-
#convert_inline_quoted(node) ⇒ Object
-
#convert_listing(node) ⇒ Object
-
#convert_olist(node) ⇒ Object
-
#convert_open(node) ⇒ Object
-
#convert_paragraph(node) ⇒ Object
Paragraphs, the three list types, and open blocks don't reliably route through arrange_block, unlike admonition/quote/verse/sidebar (widened directly into the existing arrange_block hook in change_bars.rb), so they need the same manual page-cursor bracket convert_table and convert_image use, factored out as bracket_change_bar since none of the five need any special-casing beyond that.
-
#convert_preamble(node) ⇒ Object
-
#convert_rhrev(node) ⇒ Object
-
#convert_section(node, opts = {}) ⇒ Object
-
#convert_table(node) ⇒ Object
-
#convert_ulist(node) ⇒ Object
-
#enclosing_section(node) ⇒ Object
Walks up from node to its nearest enclosing :section, or nil if none exists, i.e.
-
#initialize(*args) ⇒ Converter
constructor
A new instance of Converter.
-
#ink_pagerhref_tables ⇒ Object
-
#marked_table_cell_bar_rows(node) ⇒ Object
The 0-indexed row positions (head rows then body rows, one combined sequence, matching Prawn::Table::Cell#row) that contain at least one cell carrying the current revision's change-bar attribute, or nil if none do.
-
#node_carries_own_rhrev?(node) ⇒ Boolean
True when the node carries its own rhrev* attribute in its raw (non-inherited) attribute set -- the same check collect_revision_entries makes, hoisted out so prescan_document can gate update_revision_entry_metadata on it.
-
#preamble_rollup_node?(node) ⇒ Boolean
True when node is one of the rollup contexts and has no valid section to roll up into: no enclosing section (the document preamble), or one with no id to anchor an entry on.
-
#prescan_document(doc) ⇒ Object
-
#propagate_cell_content_marks(doc) ⇒ Object
A table cell marked with an rhrev attribute is not a rollup context (ROLLUP_CONTEXTS): a cell rolls up to its own enclosing table, not to the nearest section, and table cells are not reachable through the same context-based find_by a normal block-level node is, so this walks node.rows/[:body] directly instead.
-
#record_rhrev_body_start_page ⇒ Object
The physical PDF page where the document's own body content starts rendering, preamble if it has one, otherwise the first top-level section, captured before that content's own page advances (a chapter-opening section's own break included).
-
#revision_history ⇒ Object
-
#rhrev_display_page(physical_page_number) ⇒ Object
Converts a physical PDF page number to the reader-visible page number: a plain page number once body numbering has started, a lowercase roman numeral for anything still in the front matter.
-
#rollup_child_revision_entries(doc) ⇒ Object
Gathers every marked node across all nine rollup contexts in one unified traversal (a raw find_by predicate, not the per-context-type find_by(context:) loop above), specifically so they come back in true document order.
-
#rollup_table_cell_revision_entries(doc) ⇒ Object
-
#start_new_chapter(chapter) ⇒ Object
-
#super_convert_table(node) ⇒ Object
-
#table_contains_pagerhref?(node) ⇒ Boolean
-
#update_revision_entry_metadata(node) ⇒ Object
Methods included from ChangeBars
#arrange_block, #bracket_change_bar, #change_bar?, #change_bar_settings, #change_bar_x, #enter_change_bar_section, #exit_change_bar_section, #init_change_bars, #ink_change_bar, #ink_change_bar_for_extent, #ink_chapter_title, #ink_general_heading, #ink_part_title, #record_change_bar_start, #take_change_bar_start
Methods included from Exporter
#build_export_description_xrefs, #convert_anchor_to_xref_for_export, #export_change_text, #export_change_with_bullets, #export_to_adoc_file, #should_export_to_file?
Methods included from Renderer
#add_custom_first_row_to_markup, #allocate_revision_history_extent, #build_cell, #build_consolidated_list, #build_description_xrefs, #build_list_row, #build_location_text, #build_table_via_parsing, #build_text_cell, #build_text_row, #build_xref_text, #check_if_numbered, #create_revision_table_properly, #format_as_list, #format_location_for_display, #format_with_role, #get_column_widths, #ink_prose, #ink_revision_history, #ink_revision_history_content, #is_initial_release?, #render_adoc_include, #render_initial_release_table, #resolve_pagerhrefs_in_table, #stamp_foreground_image
#antora_build?, #convert_anchor_to_xref, #debug_log, #format_prev_rev, #needs_asciidoc_cell?, #preprocess_attribute_content, #with_attribute_missing_suppressed
Constructor Details
#initialize(*args) ⇒ Converter
Returns a new instance of Converter.
25
26
27
28
29
30
31
32
33
34
35
36
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 25
def initialize *args
super
@revision_history_extent = nil
@revision_prefix = nil
@rhrev_deferred_pages = nil
@pagerhref_tables = []
@export_completed = false
@catalog = nil
@manual_mode = false
@rhrev_body_start_page = nil
@rhrev_table_cell_bar_rows = nil
end
|
Instance Method Details
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 760
def node
@rendering_deferred_table = true
extent = dry_run onto: self do
super_convert_table node
end
@rendering_deferred_table = false
extent.each_page { |first_page| start_new_page unless first_page }
move_cursor_to extent.to.cursor
@pagerhref_tables << {
node: node,
extent: extent
}
end
|
#append_child_change_to_all(revision, change_text) ⇒ Object
Appends onto the document's existing -all change text for this
revision, or sets it fresh if none exists yet. Catalog#add_all_entry
overwrites unconditionally, so the existing value has to be read
and folded in here, unlike add_entry it has no anchor to guard
against being called more than once.
556
557
558
559
560
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 556
def append_child_change_to_all revision, change_text
existing = revision_history.all_entries[revision]
combined = existing.to_s.empty? ? change_text : "#{existing} * #{change_text}"
revision_history.add_all_entry revision, combined
end
|
#append_child_change_to_entry(revision, anchor_node, change_text) ⇒ Object
Finds anchor_node's existing entry for this revision and appends
another bullet onto its :change text, or creates the entry if
anchor_node has none yet. format_as_list (renderer.rb) already
turns a " * "-joined string into a rendered bulleted list,
including retroactively bulleting a first item that started out as
plain prose, so joining is all that's needed here, no separate
bulleting step. A node that is also independently marked already
has its own entry, with its own change text as the first item, by
the time this runs.
Generalized over anchor_node's context (section or table so far):
the section-specific fields (sectnum, is_chapter, sectname) all
respond_to?-guard to nil/false for a context that doesn't have
them, table's own caption_number is picked up the same way
update_revision_entry_metadata already does for a table marked
directly, so a synthesized table entry looks the same as one from
the normal collect_revision_entries path.
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 527
def append_child_change_to_entry revision, anchor_node, change_text
entries = revision_history.entries[revision] ||= []
entry = entries.find { |e| e[:anchor] == anchor_node.id }
if entry
entry[:change] = entry[:change].to_s.empty? ? change_text : "#{entry[:change]} * #{change_text}"
else
is_chapter = anchor_node.context == :section && anchor_node.respond_to?(:level) &&
anchor_node.document.doctype == 'book' && (anchor_node.level == 0 || anchor_node.level == 1)
caption_number = nil
if anchor_node.respond_to?(:caption) && anchor_node.caption && anchor_node.caption =~ /(\d+)/
caption_number = $1
end
revision_history.add_entry revision, anchor_node.id, change_text,
reftext: (anchor_node.respond_to?(:reftext) ? anchor_node.reftext : nil),
title: anchor_node.title,
sectnum: (anchor_node.respond_to?(:sectnum) ? anchor_node.sectnum : nil),
context: anchor_node.context,
is_chapter: is_chapter,
sectname: (anchor_node.respond_to?(:sectname) ? anchor_node.sectname : nil),
caption_number: caption_number,
source_line: anchor_node.lineno
end
end
|
#catalog_block_anchor(node) ⇒ Object
703
704
705
706
707
708
709
710
711
712
713
714
715
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 703
def catalog_block_anchor node
@anchor_catalog ||= {}
if node.id
@anchor_catalog[node.id] ||= {
title: node.title,
context: node.context
}
page_num = page_number
@anchor_catalog[node.id][:dest] = { page: (rhrev_display_page page_num), y: cursor }
revision_history.link_dest_to_page node.id, page_num, (@rhrev_body_start_page || 1), y: cursor
end
end
|
#collect_document_level_entries(doc) ⇒ Object
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 805
def collect_document_level_entries doc
return if @document_entries_collected
@document_entries_collected = true
doc.attributes.each_key do |key|
key_str = key.to_s
next unless key_str.end_with?('-prevrev')
revision = key_str.delete_suffix('-prevrev')
next unless revision.match?(/\A\d+(?:-\d+)*\z/)
all_attr_name = "#{@revision_prefix}#{revision}-all"
if (all_value = doc.attr(all_attr_name))
revision_history.add_all_entry revision, all_value
end
cover_attr_name = "#{@revision_prefix}#{revision}-cover"
if (cover_value = doc.attr(cover_attr_name))
revision_history.add_cover_entry revision, cover_value
end
end
end
|
#collect_revision_entries(node) ⇒ Object
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 372
def collect_revision_entries node
return if @prescan_complete
return unless node.respond_to?(:attributes)
is_document = node.class.to_s.include?('Document')
return if is_document
attr_entries = node.instance_variable_get(:@attributes) rescue {}
return if attr_entries.nil? || attr_entries.empty?
has_rhrev = attr_entries.keys.any? { |k| k.to_s.start_with?(@revision_prefix) }
return unless has_rhrev
has_id = node.id && !node.id.empty? rescue false
unless has_id
debug_log "Node #{node.context} has rhrev attributes but NO ID. Skipping. Attributes: #{attr_entries.keys.select{|k| k.to_s.start_with?(@revision_prefix)}}", @document
return
end
debug_log "Found entry candidate on #{node.context} id=#{node.id}", @document
attr_entries.each do |key, value|
key_str = key.to_s
next unless key_str.start_with?(@revision_prefix)
next if key_str.include?('-all') || key_str.end_with?('-cover')
revision = key_str.sub("#{@revision_prefix}", '')
debug_log "Adding entry: Rev=#{revision}, Anchor=#{node.id}, Change=#{value}", @document
revision_history.add_entry revision, node.id, value.to_s,
reftext: nil,
title: nil,
sectnum: nil,
context: node.context,
is_chapter: false,
sectname: nil,
caption_number: nil,
source_line: node.lineno
end
end
|
#convert_dlist(node) ⇒ Object
312
313
314
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 312
def convert_dlist node
bracket_change_bar(node) { super }
end
|
#convert_document(node) ⇒ Object
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 74
def convert_document node
@revision_prefix = node.attr 'revhistoryprefix', 'rhrev'
init_change_bars node
@manual_mode = node.attr('rhrev') == 'manual'
@catalog = revision_history
prescan_document node
result = super
if @revision_history_extent && @catalog
ink_revision_history node, @revision_history_extent
end
if @manual_mode && @pagerhref_tables && !@pagerhref_tables.empty?
if node.attr?('rhrev-export-to-file') && @catalog
export_to_adoc_file node
end
result
end
|
#convert_example(node) ⇒ Object
248
249
250
251
252
253
254
255
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 248
def convert_example node
collect_revision_entries node unless @prescan_complete
update_revision_entry_metadata node
result = super
catalog_block_anchor node
result
end
|
#convert_floating_title(node) ⇒ Object
266
267
268
269
270
271
272
273
274
275
276
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 266
def convert_floating_title node
collect_revision_entries node unless @prescan_complete
update_revision_entry_metadata node
result = super
catalog_block_anchor node
if @change_bar_attr && !scratch? && (start_pos = take_change_bar_start node)
ink_change_bar start_pos, { page: page_number, cursor: cursor }
end
result
end
|
#convert_image(node) ⇒ Object
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 228
def convert_image node
collect_revision_entries node unless @prescan_complete
update_revision_entry_metadata node
if @change_bar_attr && !scratch? && (change_bar? node) &&
!((node.attr 'target').to_s.downcase.end_with? '.pdf')
bar_from = { page: page_number, cursor: cursor }
end
result = super
catalog_block_anchor node
if bar_from
bar_to = { page: page_number, cursor: cursor }
bar_from = { page: bar_to[:page], cursor: bounds.top } if bar_to[:page] > bar_from[:page]
ink_change_bar bar_from, bar_to
end
result
end
|
#convert_inline_quoted(node) ⇒ Object
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 335
def convert_inline_quoted node
if node.text&.match?(/^\[pagerhref:/)
if node.text =~ /^\[pagerhref:(.+)\]$/
anchor = $1
lookup_anchor = anchor.gsub('----', ':::')
if @anchor_catalog && @anchor_catalog[lookup_anchor] && @anchor_catalog[lookup_anchor][:dest]
page_num = @anchor_catalog[lookup_anchor][:dest][:page]
%(<a anchor="#{lookup_anchor}"><span class="pagerhref">#{page_num}</span></a>)
else
if scratch?
%(<a anchor="#{lookup_anchor}"><span class="pagerhref">99</span></a>)
else
%(<a anchor="#{lookup_anchor}"><span class="pagerhref">??</span></a>)
end
end
else
super
end
else
super
end
end
|
#convert_listing(node) ⇒ Object
257
258
259
260
261
262
263
264
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 257
def convert_listing node
collect_revision_entries node unless @prescan_complete
update_revision_entry_metadata node
result = super
catalog_block_anchor node
result
end
|
#convert_olist(node) ⇒ Object
308
309
310
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 308
def convert_olist node
bracket_change_bar(node) { super }
end
|
#convert_open(node) ⇒ Object
300
301
302
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 300
def convert_open node
bracket_change_bar(node) { super }
end
|
#convert_paragraph(node) ⇒ Object
Paragraphs, the three list types, and open blocks don't reliably
route through arrange_block, unlike admonition/quote/verse/sidebar
(widened directly into the existing arrange_block hook in
change_bars.rb), so they need the same manual page-cursor bracket
convert_table and convert_image use, factored out as
bracket_change_bar since none of the five need any special-casing
beyond that. open specifically: 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 entirely, so
bracketing the whole call here covers both paths uniformly instead
of depending on which one super happens to take.
None of the nine call the collect/update/catalog trio the way
section/example/table/image/floating_title do: a marked paragraph,
list, admonition, open, quote, verse, or sidebar doesn't get its
own revision-history-table row or its own id requirement, it rolls
up as a bullet into its enclosing section's entry instead, handled
entirely at prescan time by rollup_child_revision_entries.
296
297
298
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 296
def convert_paragraph node
bracket_change_bar(node) { super }
end
|
#convert_preamble(node) ⇒ Object
107
108
109
110
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 107
def convert_preamble node
record_rhrev_body_start_page
super
end
|
#convert_rhrev(node) ⇒ Object
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 316
def convert_rhrev node
record_rhrev_body_start_page
return unless node.document.attr? 'rhrev'
return if @revision_history_extent
collect_document_level_entries node.document
@revision_history_extent = allocate_revision_history_extent node.document
nil
end
|
#convert_section(node, opts = {}) ⇒ Object
112
113
114
115
116
117
118
119
120
121
122
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
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 112
def convert_section node, opts = {}
record_rhrev_body_start_page
collect_revision_entries node unless @prescan_complete
update_revision_entry_metadata node
enter_change_bar_section node
result = super
exit_change_bar_section node
if node.id
page_num = ((node.attr 'pdf-page-start') || page_number).to_i
@anchor_catalog ||= {}
@anchor_catalog[node.id] ||= {
title: node.title,
reftext: node.reftext,
sectnum: node.sectnum,
context: node.context,
level: node.level
}
@anchor_catalog[node.id][:dest] = { page: (rhrev_display_page page_num), y: cursor }
revision_history.link_dest_to_page node.id, page_num, (@rhrev_body_start_page || 1), y: cursor
end
if @change_bar_attr && !scratch? && (start_pos = take_change_bar_start node)
ink_change_bar start_pos, { page: page_number, cursor: cursor }
end
result
end
|
#convert_table(node) ⇒ Object
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 179
def convert_table node
has_id = node.id && !node.id.empty? rescue false
has_rhrev = false
if node.respond_to?(:attributes) && node.attributes
has_rhrev = node.attributes.keys.any? { |k| k.to_s.start_with?(@revision_prefix || 'rhrev') } rescue false
end
= @manual_mode && !@rendering_deferred_table && (node)
@rhrev_table_cell_bar_rows = marked_table_cell_bar_rows(node) if @change_bar_attr && !scratch?
if !has_id && !has_rhrev && !
return super
end
collect_revision_entries node unless @prescan_complete
update_revision_entry_metadata node
if
node
else
if @change_bar_attr && !scratch? && (change_bar? node) &&
!(!at_page_top? && ((node.option? 'unbreakable') ||
((node.option? 'breakable') && (node.id || node.title?))))
bar_from = { page: page_number, cursor: cursor }
end
result = super
catalog_block_anchor node
ink_change_bar bar_from, { page: page_number, cursor: cursor } if bar_from
result
end
ensure
@rhrev_table_cell_bar_rows = nil
end
|
#convert_ulist(node) ⇒ Object
304
305
306
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 304
def convert_ulist node
bracket_change_bar(node) { super }
end
|
#enclosing_section(node) ⇒ Object
Walks up from node to its nearest enclosing :section, or nil if
none exists, i.e. node sits in the document preamble. Shared by
rollup_child_revision_entries and preamble_rollup_node? below.
438
439
440
441
442
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 438
def enclosing_section node
section = node.parent
section = section.parent until section.nil? || section.context == :section
section
end
|
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 785
def
return if scratch?
@anchor_catalog ||= {}
@pagerhref_tables.each do |table_info|
extent = table_info[:extent]
node = table_info[:node]
go_to_page extent.from.page
move_cursor_to extent.from.cursor
@rendering_deferred_table = true
super_convert_table node
@rendering_deferred_table = false
end
end
|
#marked_table_cell_bar_rows(node) ⇒ Object
The 0-indexed row positions (head rows then body rows, one combined
sequence, matching Prawn::Table::Cell#row) that contain at least
one cell carrying the current revision's change-bar attribute, or
nil if none do. Row index only, not (row, column): the bar only
cares which row, see convert_table for the full reasoning
(colspan makes a reliable column index unsafe to compute, source_
location, tried first, turned out to always be nil under normal
CLI usage and matched every cell instead of just the marked one).
Extracted as its own method specifically so it can be tested
directly: that bug was invisible to a real-render/pdftotext check,
the table's own revision-history entry was already correct either
way, only the bar geometry was wrong, and bars are graphical.
169
170
171
172
173
174
175
176
177
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 169
def marked_table_cell_bar_rows node
marked_rows = []
(node.rows[:head] + node.rows[:body]).each_with_index do |row, row_idx|
marked_rows << row_idx if row.any? do |cell|
cell.respond_to?(:attributes) && cell.attributes && (cell.attributes.key? @change_bar_attr)
end
end
marked_rows.empty? ? nil : marked_rows
end
|
#node_carries_own_rhrev?(node) ⇒ Boolean
True when the node carries its own rhrev* attribute in its raw (non-inherited)
attribute set -- the same check collect_revision_entries makes, hoisted out so
prescan_document can gate update_revision_entry_metadata on it.
419
420
421
422
423
424
425
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 419
def node_carries_own_rhrev? node
return false unless node.respond_to?(:attributes)
raw = (node.instance_variable_get(:@attributes) rescue nil)
return false if raw.nil? || raw.empty?
prefix = @revision_prefix || 'rhrev'
raw.keys.any? { |k| k.to_s.start_with?(prefix) }
end
|
#preamble_rollup_node?(node) ⇒ Boolean
True when node is one of the rollup contexts and has no valid
section to roll up into: no enclosing section (the document
preamble), or one with no id to anchor an entry on.
rollup_child_revision_entries uses this to route such a node's
change text to the document-level -all entry instead of a section
entry, since there's no section anchor available either way.
450
451
452
453
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 450
def preamble_rollup_node? node
return false unless ROLLUP_CONTEXTS.include? node.context
(section = enclosing_section node).nil? || section.id.nil?
end
|
#prescan_document(doc) ⇒ Object
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 829
def prescan_document doc
debug_log "Starting prescan_document", doc
with_attribute_missing_suppressed doc do
collect_document_level_entries doc
[:section, :floating_title, :example, :listing, :table, :image].each do |ctx|
doc.find_by(context: ctx).each do |node|
collect_revision_entries node
update_revision_entry_metadata node if node_carries_own_rhrev? node
end
end
propagate_cell_content_marks doc
rollup_child_revision_entries doc
rollup_table_cell_revision_entries doc
@prescan_complete = true
debug_log "Prescan complete. Catalog has #{revision_history.entries.values.flatten.size} entries.", doc
end
end
|
#propagate_cell_content_marks(doc) ⇒ Object
A table cell marked with an rhrev attribute is not a rollup context
(ROLLUP_CONTEXTS): a cell rolls up to its own enclosing table, not
to the nearest section, and table cells are not reachable through
the same context-based find_by a normal block-level node is, so
this walks node.rows/[:body] directly instead. Otherwise the
same three-tier fallback as rollup_child_revision_entries: append
onto the table's own entry (creating it, with full metadata, if the
table carries no rhrev attribute of its own) when the table has an
id to anchor on; otherwise the table's nearest enclosing section's
entry, the same fallback a marked paragraph or list already uses;
only when neither exists (the table sits in the document preamble,
no section at all) does this fall back to the document-level -all
entry. -all is for document-wide, structural change text, not a
substitute anchor for "this table happened not to get an id": a
table with no id of its own but sitting inside a real section
almost always has one, and a single cell's change does not belong
at the document level just because nobody bothered with an id.
Must run at prescan time, same reason as rollup_child_revision_entries:
allocate_revision_history_extent measures space for the revision-
history table before any user table has actually rendered, so a
cell-driven entry has to exist before that measurement, not merely
by the time the table itself renders.
A plain table cell's content is never parsed as blocks, so an
attribute list inside one is just literal text, and a Table::Cell's
own attributes are populated only by the fixed cell-specifier
grammar (colspan, rowspan, style, halign, valign), never by a
general attribute list. An AsciiDoc-style (a|) cell's content does
parse as blocks, so [rhrev1-2="..."] before content inside one
parses and attaches the normal way, just to that nested block, not
to the cell. That nested block is not reachable from the outer
document at all (its inner_document is a separate tree, confirmed:
find_by on the outer document returns zero results for it), so
rollup_table_cell_revision_entries and the draw-time bar patch,
which both read the cell's own attributes, would never see it.
Copies any rhrev-prefixed attribute from a marked nested block onto
the enclosing cell instead, then strips it from the nested block.
The strip matters, not just tidiness: asciidoctor-pdf renders an
a| cell's content by calling pdf.traverse on it directly
(lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb), through the
same converter, so a paragraph left marked would also
independently trigger convert_paragraph's own bar-inking hook
during that nested traversal, with cursor coordinates from inside
the cell's bounded box, not a reliable page-margin position,
alongside whatever the cell-level mechanism already draws
correctly. Stripping it after copying leaves exactly one bar,
drawn with real, resolved geometry.
Must run before rollup_table_cell_revision_entries and before real
rendering, so the cell's own attributes are already in place by
the time either reads them.
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 614
def propagate_cell_content_marks doc
doc.find_by(context: :table).each do |table|
cells = (table.rows[:head] + table.rows[:body]).flatten
cells.each do |cell|
next unless cell.style == :asciidoc
inner = (cell.inner_document rescue nil)
next unless inner
inner.find_by { |node| ROLLUP_CONTEXTS.include? node.context }.each do |node|
attr_entries = (node.instance_variable_get(:@attributes) rescue nil)
next if attr_entries.nil? || attr_entries.empty?
attr_entries.keys.each do |key|
key_str = key.to_s
next unless key_str.start_with?(@revision_prefix)
cell.set_attr key_str, attr_entries[key]
node.remove_attr key_str
end
end
end
end
end
|
#record_rhrev_body_start_page ⇒ Object
The physical PDF page where the document's own body content starts
rendering, preamble if it has one, otherwise the first top-level
section, captured before that content's own page advances (a
chapter-opening section's own break included). Mirrors the point
where asciidoctor-pdf itself samples body_start_page_number, right
before traverse renders anything (asciidoctor-pdf converter.rb, the
body_offset = (body_start_page_number = page_number) - 1 line).
Captured once, from whichever of convert_preamble/convert_section
runs first; every later call is a no-op via the memoized ivar.
Used as the start_page_number fed to Catalog#link_dest_to_page, so
entries display the reader-visible page (matching page_numbering_
start_at: body, the default), not a raw physical index that runs
ahead by however many roman-numeral-numbered front-matter pages a
title page, TOC, or preamble content added before the body starts.
Other page_numbering_start_at modes (toc, after-toc, cover, an
explicit integer) are not accounted for and stay a known limitation.
58
59
60
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 58
def record_rhrev_body_start_page
@rhrev_body_start_page ||= page_number unless scratch?
end
|
#revision_history ⇒ Object
38
39
40
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 38
def revision_history
@catalog ||= Catalog.new
end
|
#rhrev_display_page(physical_page_number) ⇒ Object
Converts a physical PDF page number to the reader-visible page
number: a plain page number once body numbering has started, a
lowercase roman numeral for anything still in the front matter.
Mirrors Catalog#link_dest_to_page's own conversion, kept in sync so
revision-history entries and pagerhref: display the same number for
the same physical page.
68
69
70
71
72
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 68
def rhrev_display_page physical_page_number
start_at = @rhrev_body_start_page || 1
virtual = physical_page_number - (start_at - 1)
(virtual < 1 ? (RomanNumeral.new physical_page_number, :lower) : virtual).to_s
end
|
#rollup_child_revision_entries(doc) ⇒ Object
Gathers every marked node across all nine rollup contexts in one
unified traversal (a raw find_by predicate, not the per-context-type
find_by(context:) loop above), specifically so they come back in
true document order. node.lineno is not a usable substitute for
sorting afterward: it is nil unless the document was parsed with
sourcemap: true, which asciidoctor-pdf's CLI does not enable, so a
sort keyed on it silently no-ops and leaves nodes grouped by
whichever context find_by(context:) happened to visit first. Get a
single predicate-based find_by to do the ordering instead of fixing
it after the fact.
True document order matters here because the per-context-type loop
above finds every paragraph in the whole document before it finds
any list, regardless of which one actually comes first inside a
given section, which would scramble bullet order within a section's
entry. This pass must still run after that loop, so a section's own
entry, if it independently carries an rhrev attribute, already
exists for a child's bullet to find and append onto instead of
racing to create a duplicate.
Content with no enclosing section, or one with no id (the document
preamble, in practice), has no section anchor to roll up into, so
its change text goes to the document-level -all entry instead:
appended onto one the document already sets via its own
rhrevN-M-all attribute, or creating one fresh if it doesn't. The
-all row already renders without a page number (the localized "All"
label takes that column instead) and already runs through the same
format_as_list bulleting as a section entry, so nothing new is
needed on the rendering side, only on which entry this appends to.
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 484
def rollup_child_revision_entries doc
nodes = doc.find_by { |node| ROLLUP_CONTEXTS.include? node.context }
nodes.select! { |node| node_carries_own_rhrev? node }
nodes.each do |node|
attr_entries = (node.instance_variable_get(:@attributes) rescue nil)
next if attr_entries.nil? || attr_entries.empty?
section = enclosing_section node
has_section = section && section.id
attr_entries.each do |key, value|
key_str = key.to_s
next unless key_str.start_with?(@revision_prefix)
next if key_str.include?('-all') || key_str.end_with?('-cover')
revision = key_str.sub("#{@revision_prefix}", '')
if has_section
append_child_change_to_entry revision, section, value.to_s
else
append_child_change_to_all revision, value.to_s
end
end
end
end
|
#rollup_table_cell_revision_entries(doc) ⇒ Object
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 637
def rollup_table_cell_revision_entries doc
doc.find_by(context: :table).each do |table|
cells = (table.rows[:head] + table.rows[:body]).flatten
cells.each do |cell|
attr_entries = (cell.instance_variable_get(:@attributes) rescue nil)
next if attr_entries.nil? || attr_entries.empty?
attr_entries.each do |key, value|
key_str = key.to_s
next unless key_str.start_with?(@revision_prefix)
next if key_str.include?('-all') || key_str.end_with?('-cover')
revision = key_str.sub("#{@revision_prefix}", '')
if table.id
append_child_change_to_entry revision, table, value.to_s
elsif (section = enclosing_section(table)) && section.id
append_child_change_to_entry revision, section, value.to_s
else
append_child_change_to_all revision, value.to_s
end
end
end
end
end
|
#start_new_chapter(chapter) ⇒ Object
359
360
361
362
363
364
365
366
367
368
369
370
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 359
def start_new_chapter chapter
if !@revision_history_extent && chapter.document && (chapter.document.attr? 'rhrev')
rhrev_value = chapter.document.attr('rhrev')
export_to_file = chapter.document.attr? 'rhrev-export-to-file'
if rhrev_value != 'macro' && rhrev_value != 'manual' && !export_to_file
collect_document_level_entries chapter.document
@revision_history_extent = allocate_revision_history_extent chapter.document
end
end
super
end
|
#super_convert_table(node) ⇒ Object
780
781
782
783
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 780
def super_convert_table node
self.class.superclass.instance_method(:convert_table).bind(self).call(node)
end
|
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 717
def node
return false unless node.context == :table
node.rows[:body].each do |row|
row.each do |cell|
if cell.instance_variable_defined?(:@text)
raw_text = cell.instance_variable_get(:@text).to_s
return true if raw_text.include?('pagerhref:')
end
if cell.instance_variable_defined?(:@inner_document)
inner_doc = cell.instance_variable_get(:@inner_document)
if inner_doc && inner_doc.instance_variable_defined?(:@lines)
lines = inner_doc.instance_variable_get(:@lines) || []
return true if lines.any? { |l| l.to_s.include?('pagerhref:') }
end
end
if cell.style == :asciidoc
if cell.instance_variable_defined?(:@inner_document)
inner = cell.instance_variable_get(:@inner_document)
if inner && inner.respond_to?(:blocks)
inner.blocks.each do |block|
if block.instance_variable_defined?(:@lines)
lines = block.instance_variable_get(:@lines) || []
return true if lines.any? { |l| l.to_s.include?('pagerhref:') }
end
end
end
end
end
end
end
false
end
|
#update_revision_entry_metadata(node) ⇒ Object
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
|
# File 'lib/asciidoctor/rhrev/converter.rb', line 662
def update_revision_entry_metadata node
return unless node.id
return unless node.respond_to?(:attributes)
with_attribute_missing_suppressed node.document do
node.attributes.each do |key, value|
key_str = key.to_s
next unless key_str.start_with?(@revision_prefix)
next if key_str.include?('-all') || key_str.end_with?('-cover')
revision = key_str.sub("#{@revision_prefix}", '')
entries = revision_history.entries[revision]
next unless entries
entry = entries.find { |e| e[:anchor] == node.id }
next unless entry
if entry[:change].to_s.empty?
entry[:change] = value
end
entry[:reftext] = node.reftext
entry[:title] = node.title
entry[:sectnum] = node.respond_to?(:sectnum) ? node.sectnum : nil
if node.context == :section && node.document.doctype == 'book'
entry[:is_chapter] = (node.level == 0 || node.level == 1)
end
if node.respond_to?(:caption) && node.caption
if node.caption =~ /(\d+)/
entry[:caption_number] = $1
end
end
entry[:sectname] = node.sectname if node.respond_to?(:sectname)
end
end
end
|