Class: Asciidoctor::ListsExtended::PDFConverterWithLists
- Inherits:
-
Object
- Object
- Asciidoctor::ListsExtended::PDFConverterWithLists
- Includes:
- PdfRenderer
- Defined in:
- lib/asciidoctor-lists-extended/pdf_converter.rb
Overview
Single unified PDF converter that replaces the four copy-pasted classes (PDFConverterWithLOF, PDFConverterWithLOT, PDFConverterWithLOE, PDFConverterWithLOL) from asciidoctor-pdf-lofte.
Hooks into asciidoctor-pdf’s allocate_toc / ink_toc lifecycle to:
1. Reserve page space for each list-of:: macro found in the document
(allocate_toc → allocate_list via dry_run).
2. Render each list with ToC-style dot leaders (ink_toc → ink_list).
3. Insert virtual Section nodes so lists appear in the PDF ToC and
bookmark outline by default. Per-macro flags exclude_from_toc and
exclude_from_outline opt individual lists out.
ink_toc_level is overridden only for list rendering (guarded by
Instance Method Summary collapse
-
#add_outline_level(outline, sections, num_levels, expand_levels) ⇒ Object
———————————————————————– add_outline_level override ———————————————————————– Filters out virtual list sections marked with ‘list-exclude-from-outline’ before delegating to the parent, which builds PDF bookmarks.
-
#allocate_toc(doc, num_levels, toc_start_cursor, break_after_toc) ⇒ Object
———————————————————————– allocate_toc hook ———————————————————————– Called by asciidoctor-pdf early in document generation to reserve page space for the ToC.
-
#convert_paragraph(node) ⇒ Object
———————————————————————– convert_paragraph override ———————————————————————– Intercepts UUID placeholder paragraphs belonging to chapter-scoped inline lists (those stored in @inline_list_configs by collect_and_allocate_lists).
-
#get_entries_for_toc(node) ⇒ Object
———————————————————————– get_entries_for_toc override ———————————————————————– Filters out: list-exclude-from-toc — virtual list sections the author opted out of the ToC list-toc-measure-skip — original sections for empty lists, temporarily marked by mark_empty_list_sections so allocate_toc’s dry-run does not over-allocate ToC pages for them.
-
#initialize(*args) ⇒ PDFConverterWithLists
constructor
A new instance of PDFConverterWithLists.
-
#ink_toc(doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0) ⇒ Object
———————————————————————– ink_toc hook ———————————————————————– Render every allocated list before delegating to super (which renders the real ToC).
-
#ink_toc_level(entries, num_levels, dot_leader, num_front_matter_pages) ⇒ Object
———————————————————————– ink_toc_level override ———————————————————————– When @rendering_list is true (set by PdfRenderer#ink_list_content), dispatch to our list-specific implementation that uses captioned_title for non-section entries.
Methods included from PdfRenderer
#get_list_entries, #ink_list_content, #insert_list_into_toc_section, #resolve_caption_style, #resolve_exclude_from_outline, #resolve_exclude_from_toc
Constructor Details
#initialize(*args) ⇒ PDFConverterWithLists
Returns a new instance of PDFConverterWithLists.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 28 def initialize(*args) super @list_extents = {} # UUID → Extent (front-matter lists only) @list_configs_ordered = [] # front-matter list configs in document order @inline_list_configs = {} # UUID → config for section-scoped inline lists @inline_render_positions = {} # UUID → deferred render position (page + cursor) @inline_num_front_matter_pages = 0 # saved from ink_toc for inline page-number math @rendering_list = false @list_strip_period = false @list_split_caption = false @list_entry_indent = nil @list_first_entry_margin = nil @list_toc_insert_idx = 0 end |
Instance Method Details
#add_outline_level(outline, sections, num_levels, expand_levels) ⇒ Object
add_outline_level override
Filters out virtual list sections marked with ‘list-exclude-from-outline’ before delegating to the parent, which builds PDF bookmarks. exclude_from_outline keeps a list in the ToC while hiding it from the outline.
173 174 175 176 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 173 def add_outline_level(outline, sections, num_levels, ) filtered = sections.reject { |s| s.attr? 'list-exclude-from-outline' } super outline, filtered, num_levels, end |
#allocate_toc(doc, num_levels, toc_start_cursor, break_after_toc) ⇒ Object
allocate_toc hook
Called by asciidoctor-pdf early in document generation to reserve page
- space for the ToC. We mark sections that contain empty list-of
-
macros
before calling super so that super’s dry-run of ink_toc does not count those sections as ToC entries and over-allocate ToC pages. Those sections are removed entirely by collect_and_allocate_lists, so the marker never reaches the final document.
67 68 69 70 71 72 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 67 def allocate_toc(doc, num_levels, toc_start_cursor, break_after_toc) mark_empty_list_sections doc result = super collect_and_allocate_lists doc, num_levels, toc_start_cursor, break_after_toc result end |
#convert_paragraph(node) ⇒ Object
convert_paragraph override
Intercepts UUID placeholder paragraphs belonging to chapter-scoped inline lists (those stored in @inline_list_configs by collect_and_allocate_lists). Renders the list entries at the current cursor position without a heading (the enclosing === section provides the heading) and without any page break before or after. All other paragraphs delegate to super unchanged.
convert_paragraph override
Intercepts UUID placeholder paragraphs for section-scoped inline lists.
Rendering strategy (two-phase):
Phase 1 — body rendering (here, scratch? may be true or false):
• When scratch? is true: call ink_toc_level in scratch mode so Prawn
measures the correct space for page-break decisions.
• When scratch? is false: dry-run the list to measure its height,
reserve that space in the document by advancing the cursor, and
record the page + cursor position in @inline_render_positions.
No visible content is written yet.
Phase 2 — ink_toc (called by asciidoctor-pdf AFTER traverse doc):
By the time ink_toc runs all pdf-page-start attributes have been set.
For each saved position, navigate there and render with real dot leaders
and page numbers. See ink_toc below.
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 205 def convert_paragraph(node) if node.content_model == :simple && node.lines.size == 1 && (config = @inline_list_configs[node.lines[0]]) uuid = node.lines[0] entries = get_list_entries(config[:scope_node], config[:element]) unless entries.empty? entries.each { |e| e.level = 2 if e.title } num_levels = @theme.toc_levels || 2 dot_leader = build_dot_leader_config(num_levels) caption_style = resolve_caption_style(config) entry_indent = config[:entry_indent] || @theme.list_of_entry_indent first_entry_margin = config[:first_entry_margin] || @theme.list_of_first_entry_margin if scratch? # Scratch pass (e.g. heading orphan detection): measure space only. begin @rendering_list = true @list_strip_period = (caption_style == 'strip') @list_split_caption = (caption_style == 'split') @list_entry_indent = entry_indent @list_first_entry_margin = first_entry_margin ink_toc_level entries, num_levels, dot_leader, 0 ensure @rendering_list = false @list_strip_period = false @list_split_caption = false @list_entry_indent = nil @list_first_entry_margin = nil end else # Real pass: measure via dry_run, reserve blank space, save position. extent = dry_run(onto: self) do begin @rendering_list = true @list_strip_period = (caption_style == 'strip') @list_split_caption = (caption_style == 'split') @list_entry_indent = entry_indent @list_first_entry_margin = first_entry_margin ink_toc_level entries, num_levels, dot_leader, 0 ensure @rendering_list = false @list_strip_period = false @list_split_caption = false @list_entry_indent = nil @list_first_entry_margin = nil end end save_page = extent.from.page save_cursor = extent.from.cursor # Reserve the space without rendering any content. extent.each_page { |first_page| start_new_page unless first_page } move_cursor_to extent.to.cursor @inline_render_positions[uuid] = { config: config, entries: entries, num_levels: num_levels, dot_leader: dot_leader, page: save_page, cursor: save_cursor, } end end return end super end |
#get_entries_for_toc(node) ⇒ Object
get_entries_for_toc override
Filters out:
list-exclude-from-toc — virtual list sections the author opted out of the ToC
list-toc-measure-skip — original sections for empty lists, temporarily marked
by mark_empty_list_sections so allocate_toc's dry-run
does not over-allocate ToC pages for them
163 164 165 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 163 def get_entries_for_toc(node) super.reject { |s| (s.attr? 'list-exclude-from-toc') || (s.attr? 'list-toc-measure-skip') } end |
#ink_toc(doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0) ⇒ Object
ink_toc hook
Render every allocated list before delegating to super (which renders the real ToC). Always inserts a virtual Section node for each list so it appears in the PDF bookmark outline. exclude_from_toc marks the section so get_entries_for_toc filters it from the visible ToC; exclude_from_outline marks it so add_outline_level skips it.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 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 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 82 def ink_toc(doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0) @inline_num_front_matter_pages = num_front_matter_pages @list_toc_insert_idx = 0 # toc-in-toc: insert the Table of Contents itself as the first entry in # the PDF ToC, before any list-of:: sections. Excluded from the bookmark # outline because asciidoctor-pdf's add_outline already adds a ToC bookmark. if (doc.attr? 'toc-in-toc') && @toc_extent toc_entry_title = doc.attr('toc-title') || 'Table of Contents' insert_list_into_toc_section doc, toc_entry_title, @toc_extent.page_range, '_toc_in_toc', @list_toc_insert_idx, pdf_dest: dest_top(@toc_extent.page_range.first), exclude_from_outline: true @list_toc_insert_idx += 1 end @list_configs_ordered.each do |config| extent = @list_extents[config[:uuid]] next unless extent ink_list doc, config, extent, num_levels, num_front_matter_pages list_title = config[:title] || config[:section_title] list_page_nums = extent.page_range list_id = "_list_of_#{config[:element].tr('-', '_')}" # Bare macros (no parent section and no title= attribute) have no # meaningful label for the ToC or outline — skip the virtual section. unless list_title.nil_or_empty? insert_list_into_toc_section doc, list_title, list_page_nums, list_id, @list_toc_insert_idx, pdf_dest: @last_list_dest || dest_top(list_page_nums.first), exclude_from_toc: resolve_exclude_from_toc(config), exclude_from_outline: resolve_exclude_from_outline(config) @list_toc_insert_idx += 1 end end result = super # Phase 2: render deferred inline section-scoped lists. # traverse doc has completed before ink_toc is called, so all # pdf-page-start attributes are now set and page numbers are real. unless @inline_render_positions.empty? @inline_render_positions.each_value do |pos| go_to_page pos[:page] move_cursor_to pos[:cursor] entries = get_list_entries(pos[:config][:scope_node], pos[:config][:element]) next if entries.empty? entry_level = (@theme.list_of_entry_level || 2).to_i entries.each { |e| e.level = entry_level if e.title } caption_style = resolve_caption_style(pos[:config]) entry_indent = pos[:config][:entry_indent] || @theme.list_of_entry_indent first_entry_margin = pos[:config][:first_entry_margin] || @theme.list_of_first_entry_margin begin @rendering_list = true @list_strip_period = (caption_style == 'strip') @list_split_caption = (caption_style == 'split') @list_entry_indent = entry_indent @list_first_entry_margin = first_entry_margin ink_toc_level entries, pos[:num_levels], pos[:dot_leader], num_front_matter_pages ensure @rendering_list = false @list_strip_period = false @list_split_caption = false @list_entry_indent = nil @list_first_entry_margin = nil end end go_to_page page_count end result end |
#ink_toc_level(entries, num_levels, dot_leader, num_front_matter_pages) ⇒ Object
ink_toc_level override
When @rendering_list is true (set by PdfRenderer#ink_list_content), dispatch to our list-specific implementation that uses captioned_title for non-section entries. Otherwise delegate to the parent unchanged so that the real Table of Contents is not affected.
50 51 52 53 54 55 56 |
# File 'lib/asciidoctor-lists-extended/pdf_converter.rb', line 50 def ink_toc_level(entries, num_levels, dot_leader, num_front_matter_pages) if @rendering_list ink_list_toc_level entries, num_levels, dot_leader, num_front_matter_pages else super end end |