Class: Uniword::Toc::TocGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/uniword/toc/toc_generator.rb

Overview

Orchestrator for Table of Contents operations.

Scans a document for heading paragraphs, builds TocEntry objects, inserts a TOC field into the document, and refreshes existing TOC fields.

Heading detection looks at paragraph style references matching “Heading1” through “Heading6” (case-insensitive, with or without space between “Heading” and the digit).

Examples:

Generate entries from headings

generator = TocGenerator.new(document)
entries = generator.generate
entries.each { |e| puts "#{e.level}: #{e.text}" }

Insert a TOC field at the beginning

generator = TocGenerator.new(document)
entries = generator.generate
generator.insert(entries, position: 0)
document.save("output.docx")

Update an existing TOC

generator = TocGenerator.new(document)
generator.update
document.save("output.docx")

Constant Summary collapse

HEADING_PATTERN =

Pattern matching heading style names like “Heading1” or “Heading 1”

/^Heading\s*(\d+)$/i
MAX_LEVEL =

Maximum heading level recognized

6

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document) ⇒ TocGenerator

Initialize the generator with a document.

Parameters:



42
43
44
# File 'lib/uniword/toc/toc_generator.rb', line 42

def initialize(document)
  @document = document
end

Instance Attribute Details

#documentWordprocessingml::DocumentRoot (readonly)

Returns The source document.

Returns:



37
38
39
# File 'lib/uniword/toc/toc_generator.rb', line 37

def document
  @document
end

Instance Method Details

#generate(max_level: MAX_LEVEL) ⇒ Array<TocEntry>

Generate TOC entries by scanning paragraphs for heading styles.

Parameters:

  • max_level (Integer) (defaults to: MAX_LEVEL)

    Maximum heading level to include (1-6)

Returns:

  • (Array<TocEntry>)

    Extracted heading entries



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/uniword/toc/toc_generator.rb', line 50

def generate(max_level: MAX_LEVEL)
  entries = []

  document.paragraphs.each_with_index do |paragraph, index|
    level = heading_level(paragraph)
    next unless level
    next if level > max_level

    text = extract_text(paragraph)
    next if text.nil? || text.strip.empty?

    style_name = resolve_style_name(paragraph)

    entries << TocEntry.new(
      level: level,
      text: text.strip,
      style_name: style_name,
      paragraph_index: index,
    )
  end

  entries
end

#insert(toc_entries, position: 0, max_level: MAX_LEVEL) ⇒ void

This method returns an undefined value.

Insert a TOC field into the document at the given position.

Creates a Structured Document Tag (SDT) containing:

  • A title paragraph (“Table of Contents”)

  • A TOC field instruction paragraph

  • Placeholder paragraphs for each entry

Parameters:

  • toc_entries (Array<TocEntry>)

    Entries to render

  • position (Integer) (defaults to: 0)

    Insert position in the paragraph list

  • max_level (Integer) (defaults to: MAX_LEVEL)

    Maximum heading level for the field code



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/uniword/toc/toc_generator.rb', line 85

def insert(toc_entries, position: 0, max_level: MAX_LEVEL)
  sdt = build_toc_sdt(toc_entries, max_level: max_level)

  body = document.body
  body.structured_document_tags << sdt

  # Add the SDT to element_order so it serializes correctly
  return unless body.element_order

  insert_element = Lutaml::Xml::Element.new("Element", "sdt")
  if position.positive? && body.element_order.size >= position
    body.element_order.insert(position, insert_element)
  else
    body.element_order.insert(0, insert_element)
  end
end

#update(max_level: MAX_LEVEL) ⇒ Array<TocEntry>

Update an existing TOC in the document.

Re-scans headings and rebuilds the first TOC SDT found. If no existing TOC is found, does nothing and returns an empty array.

Parameters:

  • max_level (Integer) (defaults to: MAX_LEVEL)

    Maximum heading level to include

Returns:

  • (Array<TocEntry>)

    Updated entries, or empty if no TOC found



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/uniword/toc/toc_generator.rb', line 109

def update(max_level: MAX_LEVEL)
  entries = generate(max_level: max_level)
  return [] if entries.empty?

  # Find the first TOC SDT in the body
  toc_sdt = find_toc_sdt
  return [] unless toc_sdt

  # Rebuild the SDT content
  rebuild_sdt_content(toc_sdt, entries, max_level: max_level)

  entries
end