Class: Uniword::Wordprocessingml::DocumentRoot

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Includes:
DocumentInput, FeatureFacade
Defined in:
lib/uniword/wordprocessingml/document_root.rb,
lib/uniword/wordprocessingml/document_root/feature_facade.rb

Overview

Root element of a WordprocessingML document

Generated from OOXML schema: wordprocessingml.yml Element: <w:document>

Defined Under Namespace

Modules: FeatureFacade

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from FeatureFacade

#accept_all_changes, #add_comment, #add_footer, #add_header, #add_watermark, #clear_comments, #diff, #extract_images, #generate_toc, #insert_image, #insert_toc, #list_comments, #list_footers, #list_headers, #list_images, #list_watermarks, #protect, #protection_active?, #protection_info, #reject_all_changes, #remove_footers, #remove_headers, #remove_image, #remove_watermark, #spellcheck, #unprotect, #update_toc, #watermark?

Instance Attribute Details

#allocatorObject

Central ID allocator — preserves IDs across build/save cycle



109
110
111
# File 'lib/uniword/wordprocessingml/document_root.rb', line 109

def allocator
  @allocator
end

#app_propertiesUniword::Ooxml::AppProperties

Get app_properties (lazy initialization)

Returns:



184
185
186
# File 'lib/uniword/wordprocessingml/document_root.rb', line 184

def app_properties
  @app_properties ||= Uniword::Ooxml::AppProperties.new
end

#bibliography_sourcesObject

Bibliography sources for sources.xml



103
104
105
# File 'lib/uniword/wordprocessingml/document_root.rb', line 103

def bibliography_sources
  @bibliography_sources
end

#bookmarksHash{String => Object}

Get bookmarks from document paragraphs

Returns:

  • (Hash{String => Object})

    Map of bookmark names to bookmark data



316
317
318
319
320
321
322
323
324
325
326
# File 'lib/uniword/wordprocessingml/document_root.rb', line 316

def bookmarks
  result = {}
  return result unless body&.paragraphs

  body.paragraphs.each do |para|
    para.bookmark_starts&.each do |bs|
      result[bs.name.to_s] = bs if bs.name
    end
  end
  result
end

#commentsObject

Additional attributes for DOCX metadata (not part of document.xml) These are stored in separate files within the DOCX package



96
97
98
# File 'lib/uniword/wordprocessingml/document_root.rb', line 96

def comments
  @comments
end

#content_typesObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def content_types
  @content_types
end

#core_propertiesUniword::Ooxml::CoreProperties

Get core_properties (lazy initialization)

Returns:



205
206
207
# File 'lib/uniword/wordprocessingml/document_root.rb', line 205

def core_properties
  @core_properties ||= Uniword::Ooxml::CoreProperties.new
end

#custom_propertiesObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def custom_properties
  @custom_properties
end

#custom_xml_itemsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def custom_xml_items
  @custom_xml_items
end

#document_relsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def document_rels
  @document_rels
end

#endnotesObject

Footnotes and endnotes (separate XML parts in DOCX package)



98
99
100
# File 'lib/uniword/wordprocessingml/document_root.rb', line 98

def endnotes
  @endnotes
end

#endnotes_relsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def endnotes_rels
  @endnotes_rels
end

#font_tableObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def font_table
  @font_table
end

#footnotesObject

Footnotes and endnotes (separate XML parts in DOCX package)



98
99
100
# File 'lib/uniword/wordprocessingml/document_root.rb', line 98

def footnotes
  @footnotes
end

#footnotes_relsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def footnotes_rels
  @footnotes_rels
end

#image_partsObject

Image parts to embed in the DOCX package Hash: r_id => { path: String, data: String, content_type: String, target: String }



101
102
103
# File 'lib/uniword/wordprocessingml/document_root.rb', line 101

def image_parts
  @image_parts
end

#numbering_configurationObject

Accessor for numbering_configuration (lazy init for builder API)



189
190
191
# File 'lib/uniword/wordprocessingml/document_root.rb', line 189

def numbering_configuration
  @numbering_configuration ||= NumberingConfiguration.new
end

#package_relsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def package_rels
  @package_rels
end

#raw_htmlObject

Additional attributes for DOCX metadata (not part of document.xml) These are stored in separate files within the DOCX package



96
97
98
# File 'lib/uniword/wordprocessingml/document_root.rb', line 96

def raw_html
  @raw_html
end

#revisionsObject

Additional attributes for DOCX metadata (not part of document.xml) These are stored in separate files within the DOCX package



96
97
98
# File 'lib/uniword/wordprocessingml/document_root.rb', line 96

def revisions
  @revisions
end

#settingsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def settings
  @settings
end

#settings_relsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def settings_rels
  @settings_rels
end

#styles_configurationObject

Lazy initialization for styles_configuration



215
216
217
# File 'lib/uniword/wordprocessingml/document_root.rb', line 215

def styles_configuration
  @styles_configuration ||= StylesConfiguration.new
end

#themeObject

Additional attributes for DOCX metadata (not part of document.xml) These are stored in separate files within the DOCX package



96
97
98
# File 'lib/uniword/wordprocessingml/document_root.rb', line 96

def theme
  @theme
end

#theme_relsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def theme_rels
  @theme_rels
end

#web_settingsObject

Round-trip parts (copied from DocxPackage during load)



105
106
107
# File 'lib/uniword/wordprocessingml/document_root.rb', line 105

def web_settings
  @web_settings
end

Instance Method Details

#apply_color_scheme(name) ⇒ self

Apply a bundled color scheme to the document's theme

Mirrors Word's Design → Colors gallery: replaces only the theme's clrScheme — fonts and formats are untouched. Creates the theme part from the bundled Office theme when the document has none.

Parameters:

  • name (String, Symbol)

    Color scheme slug (data/color_schemes/)

Returns:

  • (self)

    For method chaining

Raises:

  • (ArgumentError)

    if the scheme is unknown



406
407
408
409
410
411
412
413
# File 'lib/uniword/wordprocessingml/document_root.rb', line 406

def apply_color_scheme(name)
  colors = Resource::ColorSchemeLoader.load(name.to_s)
  transformation = Themes::ThemeTransformation.new
  ensure_theme!(transformation)
  theme.theme_elements.clr_scheme =
    transformation.build_color_scheme(colors)
  self
end

#apply_font_scheme(name) ⇒ self

Apply a bundled font scheme to the document's theme

Mirrors Word's Design → Fonts gallery: replaces only the theme's fontScheme — colors and formats are untouched. Creates the theme part from the bundled Office theme when the document has none.

Parameters:

  • name (String, Symbol)

    Font scheme slug (data/font_schemes/)

Returns:

  • (self)

    For method chaining

Raises:

  • (ArgumentError)

    if the scheme is unknown



388
389
390
391
392
393
394
395
# File 'lib/uniword/wordprocessingml/document_root.rb', line 388

def apply_font_scheme(name)
  fonts = Resource::FontSchemeLoader.load(name.to_s)
  transformation = Themes::ThemeTransformation.new
  ensure_theme!(transformation)
  theme.theme_elements.font_scheme =
    transformation.build_font_scheme(fonts)
  self
end

#apply_page_setup(size: nil, orientation: nil, margins: nil, top: nil, right: nil, bottom: nil, left: nil) ⇒ Integer

Apply uniform page setup to every section of the document

Mirrors Word's Layout dialog: named paper sizes, orientation (with Word-style dimension swap), and margins.

Parameters:

  • size (String, nil) (defaults to: nil)

    Paper size: letter, legal, a4, a5, executive

  • orientation (String, nil) (defaults to: nil)

    "portrait" or "landscape"

  • margins (Integer, String, nil) (defaults to: nil)

    Uniform margin for all sides (twips, or "1in" / "2.5cm" / "25mm")

  • top,

    right, bottom, left [Integer, String, nil] Per-side margin overrides

Returns:

  • (Integer)

    Number of sections updated



444
445
446
447
448
449
450
# File 'lib/uniword/wordprocessingml/document_root.rb', line 444

def apply_page_setup(size: nil, orientation: nil, margins: nil,
                     top: nil, right: nil, bottom: nil, left: nil)
  setup = PageSetup.new(size: size, orientation: orientation,
                        margins: margins, top: top, right: right,
                        bottom: bottom, left: left)
  setup.apply(self)
end

#apply_styles_from(source_path, strategy: :keep_existing) ⇒ self

Apply styles from another document

Parameters:

  • source_path (String)

    Path to source .docx file

  • strategy (Symbol) (defaults to: :keep_existing)

    Conflict resolution strategy

Returns:

  • (self)

    For method chaining



515
516
517
518
519
520
# File 'lib/uniword/wordprocessingml/document_root.rb', line 515

def apply_styles_from(source_path, strategy: :keep_existing)
  source_doc = Uniword.load(source_path)
  styles_configuration.merge(source_doc.styles_configuration,
                             conflict_resolution: strategy)
  self
end

#apply_styleset(name, strategy: :keep_existing) ⇒ self

Apply StyleSet to document

Parameters:

  • name (String, Symbol)

    StyleSet slug (e.g., 'signature', 'heritage')

  • strategy (Symbol) (defaults to: :keep_existing)

    Application strategy (:keep_existing, :replace, :rename)

Returns:

  • (self)

    For method chaining



373
374
375
376
377
# File 'lib/uniword/wordprocessingml/document_root.rb', line 373

def apply_styleset(name, strategy: :keep_existing)
  styleset = Uniword::Stylesets::YamlStyleSetLoader.load_bundled(name.to_s)
  styleset.apply_to(self, strategy: strategy)
  self
end

#apply_template(template_path, strategy: :keep_existing) ⇒ self

Apply both theme and styles from a template document

Parameters:

  • template_path (String)

    Path to template .docx file

  • strategy (Symbol) (defaults to: :keep_existing)

    Conflict resolution strategy for styles

Returns:

  • (self)

    For method chaining



527
528
529
530
531
532
533
# File 'lib/uniword/wordprocessingml/document_root.rb', line 527

def apply_template(template_path, strategy: :keep_existing)
  template_doc = Uniword.load(template_path)
  self.theme = template_doc.theme.dup if template_doc.theme
  styles_configuration.merge(template_doc.styles_configuration,
                             conflict_resolution: strategy)
  self
end

#apply_theme(name, **options) ⇒ self

Apply theme to document

Applies a Uniword theme by name, updating doc defaults and built-in heading/hyperlink styles to reference the theme.

Parameters:

  • name (String, Symbol)

    Theme slug (e.g., 'meridian', 'corporate')

  • options (Hash)

    optional overrides

Options Hash (**options):

  • :colors (Hash)

    override specific color keys

  • :major_font (String)

    override major font

  • :minor_font (String)

    override minor font

Returns:

  • (self)

    For method chaining



339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/uniword/wordprocessingml/document_root.rb', line 339

def apply_theme(name, **options)
  friendly = Themes::Theme.load(name.to_s)

  options[:colors]&.each do |key, value|
    friendly.color_scheme[key.to_s] = value
  end
  friendly.font_scheme.major_font = options[:major_font] if options[:major_font]
  friendly.font_scheme.minor_font = options[:minor_font] if options[:minor_font]

  word_theme = Themes::ThemeTransformation.new.to_word(friendly)
  Themes::ThemeApplicator.new.apply(word_theme, self)
  self
end

#apply_theme_file(path, variant: nil) ⇒ self

Apply theme from .thmx file

Parameters:

  • path (String)

    Path to .thmx file

  • variant (String, Integer, nil) (defaults to: nil)

    Optional variant

Returns:

  • (self)

    For method chaining



358
359
360
361
362
363
364
365
366
# File 'lib/uniword/wordprocessingml/document_root.rb', line 358

def apply_theme_file(path, variant: nil)
  loader = Themes::ThemeLoader.new
  self.theme = if variant
                 loader.load_with_variant(path, variant)
               else
                 loader.load(path)
               end
  self
end

#apply_theme_from(source_path) ⇒ self

Apply theme from another document

Parameters:

  • source_path (String)

    Path to source .docx file

Returns:

  • (self)

    For method chaining



504
505
506
507
508
# File 'lib/uniword/wordprocessingml/document_root.rb', line 504

def apply_theme_from(source_path)
  source_doc = Uniword.load(source_path)
  self.theme = source_doc.theme.dup if source_doc.theme
  self
end

#auto_transition_themeHash?

Auto-transition from MS theme to Uniword equivalent

Detects the MS theme in the document's embedded theme and replaces it with the corresponding Uniword theme (font-substituted, renamed).

Examples:

result = doc.auto_transition_theme
puts "Transitioned from #{result[:ms_name]} to #{result[:uniword_slug]}"

Returns:

  • (Hash, nil)

    { uniword_slug:, ms_name: } or nil if no match



496
497
498
# File 'lib/uniword/wordprocessingml/document_root.rb', line 496

def auto_transition_theme
  Resource::ThemeTransition.auto_transition!(self)
end

#chart_partsDocx::PartCollection

Chart parts to embed in the DOCX package, keyed by rId.

Returns:



160
161
162
# File 'lib/uniword/wordprocessingml/document_root.rb', line 160

def chart_parts
  @chart_parts ||= Docx::PartCollection.new(:r_id, Docx::ChartPart)
end

#chart_parts=(value) ⇒ Object

Bulk-assign chart parts (Hash of rId => ChartPart/hash; nil clears).



165
166
167
# File 'lib/uniword/wordprocessingml/document_root.rb', line 165

def chart_parts=(value)
  chart_parts.replace_all(value)
end

#document_statsHash

Returns Document statistics (paragraphs, tables, images).

Returns:

  • (Hash)

    Document statistics (paragraphs, tables, images)



282
283
284
285
286
287
288
# File 'lib/uniword/wordprocessingml/document_root.rb', line 282

def document_stats
  {
    paragraphs: paragraphs.count,
    tables: tables.count,
    images: images.count,
  }
end

#embeddingsDocx::PartCollection

OLE/embedded object binaries (word/embeddings/*), keyed by target.

Returns:



172
173
174
# File 'lib/uniword/wordprocessingml/document_root.rb', line 172

def embeddings
  @embeddings ||= Docx::PartCollection.new(:target, Docx::Part)
end

#embeddings=(value) ⇒ Object

Bulk-assign embeddings (Hash of target => Part/binary; nil clears).



177
178
179
# File 'lib/uniword/wordprocessingml/document_root.rb', line 177

def embeddings=(value)
  embeddings.replace_all(value)
end

#footersDocx::HeaderFooterView

Footers view over the unified store.



147
148
149
150
# File 'lib/uniword/wordprocessingml/document_root.rb', line 147

def footers
  @footers ||=
    Docx::HeaderFooterView.new(header_footer_parts, :footer)
end

#footers=(value) ⇒ Object

Bulk-assign footers (see #headers=).



153
154
155
# File 'lib/uniword/wordprocessingml/document_root.rb', line 153

def footers=(value)
  footers.replace(value)
end

Unified header/footer part store — the single storage path for both round-tripped and builder-created headers/footers.

Returns:



119
120
121
# File 'lib/uniword/wordprocessingml/document_root.rb', line 119

def header_footer_parts
  @header_footer_parts ||= Docx::HeaderFooterPartCollection.new
end

Replace the whole store (accepts HeaderFooterPart objects and legacy part hashes).



125
126
127
# File 'lib/uniword/wordprocessingml/document_root.rb', line 125

def header_footer_parts=(parts)
  header_footer_parts.replace_all(parts)
end

#headersDocx::HeaderFooterView

Headers view over the unified store (Hash-style by sectPr type, Array-style over parts).



133
134
135
136
# File 'lib/uniword/wordprocessingml/document_root.rb', line 133

def headers
  @headers ||=
    Docx::HeaderFooterView.new(header_footer_parts, :header)
end

#headers=(value) ⇒ Object

Bulk-assign headers (nil clears; Hash of type => model; Array of models/parts).



140
141
142
# File 'lib/uniword/wordprocessingml/document_root.rb', line 140

def headers=(value)
  headers.replace(value)
end

#imagesArray<Drawing>

Get all drawings (image references) from the document. Walks all paragraphs and collects Drawing elements from runs.

Returns:

  • (Array<Drawing>)

    All drawing elements in document



273
274
275
276
277
278
279
# File 'lib/uniword/wordprocessingml/document_root.rb', line 273

def images
  return [] unless body&.paragraphs

  body.paragraphs.flat_map do |para|
    (para.runs || []).flat_map(&:drawings)
  end.compact
end

#inspectString

Custom inspect for readable output

Returns:

  • (String)

    Human-readable representation



538
539
540
# File 'lib/uniword/wordprocessingml/document_root.rb', line 538

def inspect
  "#<#{self.class} @body=...>"
end

#numbering_configuration_loaded?Boolean

Whether numbering_configuration was explicitly loaded from source (vs lazily created for builder API)

Returns:

  • (Boolean)


198
199
200
# File 'lib/uniword/wordprocessingml/document_root.rb', line 198

def numbering_configuration_loaded?
  !!@numbering_configuration
end

#paragraphsArray<Paragraph>

Get all paragraphs (convenience accessor)

Returns:

  • (Array<Paragraph>)

    All paragraphs in document



258
259
260
# File 'lib/uniword/wordprocessingml/document_root.rb', line 258

def paragraphs
  body&.paragraphs || []
end

#remove_style(style_id) ⇒ Boolean

Remove one style from the document's style definitions

Default styles (w:default="1") are never removed.

Parameters:

  • style_id (String)

    Style id (w:styleId)

Returns:

  • (Boolean)

    true when the style was removed



458
459
460
# File 'lib/uniword/wordprocessingml/document_root.rb', line 458

def remove_style(style_id)
  StyleCleanup.new(self).remove?(style_id)
end

#remove_unused_stylesArray<String>

Remove every style that no content references — Word's Styles-pane decluttering as one call. Default styles are kept.

Returns:

  • (Array<String>)

    Ids of removed styles



482
483
484
# File 'lib/uniword/wordprocessingml/document_root.rb', line 482

def remove_unused_styles
  StyleCleanup.new(self).remove_unused
end

#rename_style(identifier, new_name) ⇒ Style?

Rename a style's display name (w:name) — Word's style rename. References (pStyle/rStyle/tblStyle) target the styleId, so they keep working unchanged.

Parameters:

  • identifier (String)

    Style id (w:styleId) or current display name (w:name)

  • new_name (String)

    New display name

Returns:

  • (Style, nil)

    The renamed style, or nil when not found



470
471
472
473
474
475
476
# File 'lib/uniword/wordprocessingml/document_root.rb', line 470

def rename_style(identifier, new_name)
  style = styles_configuration.style(identifier)
  return unless style

  style.name = StyleName.new(val: new_name)
  style
end

#replace_font(from:, to:) ⇒ Integer

Replace one font family with another across the document

Mirrors Word's Home → Replace → Replace Fonts: rewrites rFonts references in styles and defaults, body content, headers, footers, notes, comments, and numbering. Theme font references (asciiTheme etc.) are untouched — use #apply_font_scheme for those.

Parameters:

  • from (String)

    Font family to replace (exact match)

  • to (String)

    Replacement font family

Returns:

  • (Integer)

    Number of rFonts attribute values rewritten



426
427
428
429
# File 'lib/uniword/wordprocessingml/document_root.rb', line 426

def replace_font(from:, to:)
  replacer = FontReplacer.new(from: from, to: to)
  replacer.replace(self)
end

#save(path, format: :auto, profile: nil, validate: nil) ⇒ void

This method returns an undefined value.

Save document to file

Parameters:

  • path (String)

    Output file path

  • format (Symbol) (defaults to: :auto)

    Format (:docx, :mhtml, :auto)

  • profile (Docx::Profile, nil) (defaults to: nil)

    Profile for reconciliation

  • validate (Boolean, nil) (defaults to: nil)

    Run the package integrity gate before writing; nil falls back to Uniword.configuration.validate_on_save



230
231
232
233
# File 'lib/uniword/wordprocessingml/document_root.rb', line 230

def save(path, format: :auto, profile: nil, validate: nil)
  writer = DocumentWriter.new(self)
  writer.save(path, format: format, profile: profile, validate: validate)
end

#tablesArray<Table>

Get all tables (convenience accessor)

Returns:

  • (Array<Table>)

    All tables in document



265
266
267
# File 'lib/uniword/wordprocessingml/document_root.rb', line 265

def tables
  body&.tables || []
end

#textString

Get all paragraph text

Returns:

  • (String)

    Combined text from all paragraphs



249
250
251
252
253
# File 'lib/uniword/wordprocessingml/document_root.rb', line 249

def text
  return "" unless body&.paragraphs

  body.paragraphs.map(&:text).join("\n")
end

#titleObject

Get document title (delegates to core_properties)



210
211
212
# File 'lib/uniword/wordprocessingml/document_root.rb', line 210

def title
  core_properties.title
end

#to_file(path, profile: nil, validate: nil) ⇒ void

This method returns an undefined value.

Save document to DOCX file using DocxPackage

Parameters:

  • path (String)

    Output file path

  • profile (Docx::Profile, nil) (defaults to: nil)

    Profile for reconciliation

  • validate (Boolean, nil) (defaults to: nil)

    Run the package integrity gate before writing; nil falls back to Uniword.configuration.validate_on_save



242
243
244
# File 'lib/uniword/wordprocessingml/document_root.rb', line 242

def to_file(path, profile: nil, validate: nil)
  Docx::Package.to_file(self, path, profile: profile, validate: validate)
end

#to_html_documentString

Convert OOXML document to HTML document

Returns:

  • (String)

    HTML document content



545
546
547
# File 'lib/uniword/wordprocessingml/document_root.rb', line 545

def to_html_document
  Uniword::Transformation::OoxmlToHtmlConverter.document_to_html(self)
end

#to_xml(options = {}) ⇒ Object

Override to_xml to sync element_order on child collections before serialization. lutaml-model's compiled serializer may bypass child #to_xml when serializing nested elements, so we sync here at the DocumentRoot level.



87
88
89
90
91
92
# File 'lib/uniword/wordprocessingml/document_root.rb', line 87

def to_xml(options = {})
  body&.sync_element_order_for_serialization
  footnotes&.sync_element_order if footnotes
  endnotes&.sync_element_order if endnotes
  super
end

#valid?Boolean

Check if document structure is valid. Runs model-level validation rules via Validation::Engine. Use the verify CLI command for full OPC + XSD + semantic validation.

Returns:

  • (Boolean)

    true if document has valid structure



295
296
297
# File 'lib/uniword/wordprocessingml/document_root.rb', line 295

def valid?
  validation_errors.empty?
end

#validation_errorsArray<String>

Get structural validation errors.

Returns:

  • (Array<String>)

    Error messages



302
303
304
# File 'lib/uniword/wordprocessingml/document_root.rb', line 302

def validation_errors
  structural_issues.select(&:error?).map(&:message)
end

#validation_warningsArray<String>

Get structural validation warnings.

Returns:

  • (Array<String>)

    Warning messages



309
310
311
# File 'lib/uniword/wordprocessingml/document_root.rb', line 309

def validation_warnings
  structural_issues.reject(&:error?).map(&:message)
end