Module: Asciidoctor::ListsExtended::PdfRenderer

Included in:
PDFConverterWithLists
Defined in:
lib/asciidoctor-lists-extended/pdf_renderer.rb

Overview

Mixed into PDFConverterWithLists to provide list-rendering helpers. All methods here run in the context of the PDF converter instance, so asciidoctor-pdf methods (ink_general_heading, theme_font_cascade, add_dest_for_block, ink_toc_level, etc.) are available via self.

Instance Method Summary collapse

Instance Method Details

#get_list_entries(scope_node, element_type) ⇒ Object

Collect all captioned/titled elements of the requested type from scope_node. scope_node is either the whole document (document-wide) or a chapter section (chapter-scoped), as determined by scope_node_for at collection time.



83
84
85
86
87
# File 'lib/asciidoctor-lists-extended/pdf_renderer.rb', line 83

def get_list_entries(scope_node, element_type)
  scope_node
    .find_by(traverse_documents: true, context: element_type.to_sym)
    .select { |e| (e.caption || e.title) && !e.has_role?('exclude-from-listof') }
end

#ink_list_content(doc, config, entries, num_levels, num_front_matter_pages = 0) ⇒ Object

Render one list section: title heading + dot-leader entry rows. Called both during the dry run (scratch? == true) and the real ink pass.

config keys used here:

:element        — element type string ('image', 'table', …)
:title          — explicit title override from macro attribute
:section_title  — title of the parent section captured before it was removed

Sets @rendering_list = true around the ink_toc_level call so that PDFConverterWithLists#ink_toc_level activates the list-aware code path.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/asciidoctor-lists-extended/pdf_renderer.rb', line 20

def ink_list_content(doc, config, entries, num_levels, num_front_matter_pages = 0)
  element_type = config[:element]
  dest_id      = "_list_of_#{element_type.tr('-', '_')}"

  list_title = config[:title] || config[:section_title]
  unless list_title.nil_or_empty?
    # heading level: theme key list_of_title_heading_level,
    # default 1 (heading_h1 — matches == chapter headings).
    # Set to 2 to restore the pre-1.1.1 behaviour (heading_h2).
    hlevel = (@theme.list_of_title_heading_level || 1).to_i
    hn_align_key = :"heading_h#{hlevel}_text_align"
    # Theme key mirrors asciidoctor-pdf-lofte convention, e.g. :image_list_title
    theme_key_sym = :"#{element_type.tr('-', '_')}_list_title"
    theme_font_cascade [[:heading, level: hlevel], theme_key_sym] do
      align_method = :"#{theme_key_sym}_text_align"
      title_align  = (@theme.respond_to?(align_method) ? @theme.send(align_method) : nil) ||
                     (@theme.respond_to?(hn_align_key) ? @theme.send(hn_align_key) : nil) ||
                     @theme.heading_text_align    ||
                     @base_text_align
      ink_general_heading doc, list_title,
        align:   title_align.to_sym,
        level:   hlevel,
        outdent: true,
        role:    theme_key_sym
      add_dest_for_block doc, id: dest_id, y: (at_page_top? ? page_height : nil)
      @last_list_dest = doc.attr('pdf-destination') unless scratch?
    end
  end

  # Render any extra blocks from the original section (admonitions, paragraphs,
  # etc.) that were captured alongside the list-of:: macro.  They appear after
  # the heading but before the dot-leader entry rows.  Running during both the
  # dry-run and real pass ensures the allocated space is correct.
  (config[:section_blocks] || []).each { |b| convert b }

  unless num_levels < 0 || entries.empty?
    dot_leader = build_dot_leader_config(num_levels)
    theme_margin :toc, :top

    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

    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, 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
end

#insert_list_into_toc_section(doc, list_title, list_page_nums, list_id, insert_idx, pdf_dest: nil, exclude_from_toc: false, exclude_from_outline: false) ⇒ Object

Insert a virtual Section node into the document AST at the correct position so that asciidoctor-pdf’s get_entries_for_toc picks it up and renders it in the Table of Contents.

insert_idx is the 0-based position among sibling list sections being inserted, which preserves document order in the ToC. pdf_dest: when nil (the default for regular lists), the outline falls back to get_dest(dest_name_for_id(list_id)) which resolves to the exact heading Y position registered by add_dest_for_block in ink_list_content. Pass an explicit dest_top value only for entries that have no add_dest_for_block call (e.g. toc-in-toc).



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
# File 'lib/asciidoctor-lists-extended/pdf_renderer.rb', line 100

def insert_list_into_toc_section(doc, list_title, list_page_nums, list_id, insert_idx,
    pdf_dest: nil, exclude_from_toc: false, exclude_from_outline: false)
  if (doc.attr? 'toc-placement', 'macro') && (toc_node = (doc.find_by context: :toc)[0])
    if (parent_section = toc_node.parent).context == :section
      grandparent_section = parent_section.parent
      toc_level           = parent_section.level
      base_idx            = (grandparent_section.blocks.index parent_section) + 1
    else
      grandparent_section = doc
      toc_level           = doc.sections[0]&.level || 1
      base_idx            = 0
    end
  else
    grandparent_section = doc
    toc_level           = doc.sections[0]&.level || 1
    base_idx            = 0
  end

  # If a virtual section with this id was already inserted during an earlier
  # pass (e.g. scratch before final), refresh it in place rather than
  # inserting a duplicate that would skew ToC height and entry count.
  if (existing = grandparent_section.blocks.find { |b| b.context == :section && b.id == list_id })
    existing.title = list_title
    existing.set_attr 'pdf-destination', pdf_dest if pdf_dest
    existing.set_attr 'pdf-page-start', list_page_nums.first
    existing.set_attr 'list-virtual-section', ''
    exclude_from_toc     ? existing.set_attr('list-exclude-from-toc', '')     : existing.remove_attr('list-exclude-from-toc')
    exclude_from_outline ? existing.set_attr('list-exclude-from-outline', '') : existing.remove_attr('list-exclude-from-outline')
    return existing
  end

  list_section       = Asciidoctor::Section.new grandparent_section, toc_level, false
  list_section.set_attr 'pdf-destination', pdf_dest if pdf_dest
  list_section.title = list_title
  list_section.id    = list_id
  list_section.set_attr 'pdf-page-start', list_page_nums.first
  list_section.set_attr 'list-virtual-section', ''
  list_section.set_attr 'list-exclude-from-toc',     '' if exclude_from_toc
  list_section.set_attr 'list-exclude-from-outline', '' if exclude_from_outline
  grandparent_section.blocks.insert base_idx + insert_idx, list_section
  list_section
end

#resolve_caption_style(config) ⇒ Object

Resolve the effective caption_style for a list config. Priority: macro attribute > theme key > built-in default (‘default’).



145
146
147
148
149
# File 'lib/asciidoctor-lists-extended/pdf_renderer.rb', line 145

def resolve_caption_style(config)
  return 'split' if config[:split_caption]
  return 'strip' if config[:strip_period]
  @theme.list_of_caption_style || 'default'
end

#resolve_exclude_from_outline(config) ⇒ Object

Resolve whether a list should be excluded from the PDF outline. Priority: macro flag > per-element theme key > false.



161
162
163
164
165
# File 'lib/asciidoctor-lists-extended/pdf_renderer.rb', line 161

def resolve_exclude_from_outline(config)
  return true if config[:exclude_from_outline]
  element_key = :"list_of_#{config[:element].tr('-', '_')}_exclude_from_outline"
  @theme.respond_to?(element_key) ? @theme.send(element_key) : false
end

#resolve_exclude_from_toc(config) ⇒ Object

Resolve whether a list should be excluded from the ToC. Priority: macro flag > per-element theme key > false.



153
154
155
156
157
# File 'lib/asciidoctor-lists-extended/pdf_renderer.rb', line 153

def resolve_exclude_from_toc(config)
  return true if config[:exclude_from_toc]
  element_key = :"list_of_#{config[:element].tr('-', '_')}_exclude_from_toc"
  @theme.respond_to?(element_key) ? @theme.send(element_key) : false
end