Class: Uniword::Builder::DocumentBuilder
- Inherits:
-
BaseBuilder
- Object
- BaseBuilder
- Uniword::Builder::DocumentBuilder
- Defined in:
- lib/uniword/builder/document_builder.rb
Overview
Builds and configures DocumentRoot objects.
Top-level builder for creating Word documents.
Instance Attribute Summary collapse
-
#allocator ⇒ Object
readonly
Returns the value of attribute allocator.
Attributes inherited from BaseBuilder
Class Method Summary collapse
- .default_model_class ⇒ Object
-
.from_file(path) ⇒ DocumentBuilder
Load a document from file for manipulation.
-
.from_template(path) ⇒ DocumentBuilder
Load a DOCX as a template, reset its body to a clean state, and seed the IdAllocator from the template's existing relationships.
Instance Method Summary collapse
-
#<<(element) ⇒ self
Append a top-level element (paragraph or table).
-
#apply_styleset(name, strategy: :keep_existing) ⇒ self
Apply a bundled styleset to the document.
- #author(value) ⇒ Object
-
#bibliography(style: "APA") {|BibliographyBuilder| ... } ⇒ BibliographyBuilder
Create and configure bibliography sources.
-
#bibliography_placeholder ⇒ self
Insert a bibliography placeholder (SDT content control).
-
#bookmark(name) {|ParagraphBuilder| ... } ⇒ ParagraphBuilder
Create a bookmark wrapping the next content.
-
#bullet_list {|ListBuilder| ... } ⇒ ListBuilder
Shorthand: create a bullet list.
-
#chart(type: :bar, width: nil, height: nil) {|ChartBuilder| ... } ⇒ ChartBuilder
Insert a chart into the document.
-
#comment(author:, text: nil, initials: nil, on: nil) {|CommentBuilder| ... } ⇒ Comment
Create a comment, register it in the document's comments collection (serialized to word/comments.xml on save), and anchor it around the most recently added body paragraph with commentRangeStart/commentRangeEnd markers and a CommentReference run.
-
#content_control(tag: nil, alias_name: nil, placeholder_text: nil) ⇒ SdtBuilder
Create a text content control paragraph.
- #created(value) ⇒ Object
-
#date_field(format: "M/d/yyyy") ⇒ self
Insert a date paragraph.
-
#define_style(name, base_on: "Normal") {|StyleBuilder| ... } ⇒ StyleBuilder
Define a paragraph style.
- #description(value) ⇒ Object
-
#endnote(text = nil) {|ParagraphBuilder| ... } ⇒ Wordprocessingml::Run
Create an endnote and return a Run with an endnoteReference.
-
#floating_image(path, width: nil, height: nil, alt_text: nil, align: nil, vertical_align: nil, wrap: :square, behind_text: false) ⇒ self
Insert a floating image paragraph.
-
#footer(type: "default") {|HeaderFooterBuilder| ... } ⇒ HeaderFooterBuilder
Configure a footer.
-
#footnote(text = nil) {|ParagraphBuilder| ... } ⇒ Wordprocessingml::Run
Create a footnote and return a Run with a footnoteReference.
-
#header(type: "default") {|HeaderFooterBuilder| ... } ⇒ HeaderFooterBuilder
Configure a header.
-
#heading(text, level: 1) {|ParagraphBuilder| ... } ⇒ ParagraphBuilder
Create and add a heading paragraph.
-
#horizontal_rule(style: "single", color: "auto", size: 6) ⇒ self
Insert a horizontal rule (paragraph with bottom border).
-
#image(path, width: nil, height: nil, alt_text: nil) ⇒ self
Insert an inline image paragraph.
-
#initialize(model = nil, allocator: nil) ⇒ DocumentBuilder
constructor
Every DocumentBuilder carries an IdAllocator — the single ID authority — so build-time allocations (images, hyperlinks, paragraph ids) and the save path draw from one registry.
- #keywords(value) ⇒ Object
-
#list(type: :bullet) {|ListBuilder| ... } ⇒ ListBuilder
Create a list (bulleted or numbered).
- #modified(value) ⇒ Object
-
#numbered_list {|ListBuilder| ... } ⇒ ListBuilder
Shorthand: create a numbered list.
-
#page_break ⇒ self
Insert a page break.
-
#page_number ⇒ self
Insert a page number paragraph.
-
#paragraph(text = nil) {|ParagraphBuilder| ... } ⇒ ParagraphBuilder
Create and add a paragraph to the document.
-
#save(path) ⇒ Object
Save document to file.
-
#section(type: "nextPage") {|SectionBuilder| ... } ⇒ SectionBuilder
Configure section properties.
- #subject(value) ⇒ Object
-
#table {|TableBuilder| ... } ⇒ TableBuilder
Create and add a table to the document.
-
#theme(name = nil) {|ThemeBuilder| ... } ⇒ ThemeBuilder
Apply or configure a document theme.
-
#time_field(format: "h:mm:ss am/pm") ⇒ self
Insert a time paragraph.
- #title(value) ⇒ Object
-
#toc(title: "Table of Contents", styles: nil) ⇒ self
Insert a Table of Contents.
-
#total_pages ⇒ self
Insert a total pages paragraph.
-
#watermark(text, font: "Calibri", size: 60, color: nil, opacity: "0.3", angle: -45)) ⇒ self
Add a watermark to the document header.
Methods inherited from BaseBuilder
Constructor Details
#initialize(model = nil, allocator: nil) ⇒ DocumentBuilder
Every DocumentBuilder carries an IdAllocator — the single ID authority — so build-time allocations (images, hyperlinks, paragraph ids) and the save path draw from one registry. Reuses an explicit allocator or one already on the model; otherwise populates one seeded from the model's current relationships (loaded documents keep their rIds; fresh models start empty).
45 46 47 48 49 50 51 |
# File 'lib/uniword/builder/document_builder.rb', line 45 def initialize(model = nil, allocator: nil) super(model) @allocator = allocator || @model.allocator || Docx::IdAllocator.populate_from_package(@model) @model.allocator = @allocator @footnote_builder = FootnoteBuilder.new(self, allocator: @allocator) end |
Instance Attribute Details
#allocator ⇒ Object (readonly)
Returns the value of attribute allocator.
32 33 34 |
# File 'lib/uniword/builder/document_builder.rb', line 32 def allocator @allocator end |
Class Method Details
.default_model_class ⇒ Object
34 35 36 |
# File 'lib/uniword/builder/document_builder.rb', line 34 def self.default_model_class Wordprocessingml::DocumentRoot end |
.from_file(path) ⇒ DocumentBuilder
Load a document from file for manipulation
57 58 59 |
# File 'lib/uniword/builder/document_builder.rb', line 57 def self.from_file(path) new(Uniword.load(path)) end |
.from_template(path) ⇒ DocumentBuilder
Load a DOCX as a template, reset its body to a clean state, and seed the IdAllocator from the template's existing relationships.
Use this when you want the template's OOXML scaffolding (settings, fonts, styles, theme, content_types, namespace declarations) but will replace the document body with new content. This is the recommended path to producing Word-valid output: starting from a known-good DOCX preserves mc:Ignorable prefixes, rsid values, and other structural guarantees that Word expects.
Resets performed:
- Body content cleared (paragraphs, tables, SDTs, bookmarks, element_order, section_properties)
- User footnotes/endnotes cleared (separator/continuation kept)
- custom_properties and custom_xml_items cleared
- Stale image and customXml relationships removed
- IdAllocator seeded from document_rels and package_rels
82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/uniword/builder/document_builder.rb', line 82 def self.from_template(path) raise ArgumentError, "Template not found: #{path}" unless File.exist?(path) root = Uniword.load(path) reset_template_body(root) clear_user_notes(root) root.custom_properties = nil root.custom_xml_items = nil remove_stale_relationships(root) seed_allocator(root) new(root, allocator: root.allocator) end |
Instance Method Details
#<<(element) ⇒ self
Append a top-level element (paragraph or table)
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/uniword/builder/document_builder.rb', line 149 def <<(element) case element when Wordprocessingml::Paragraph @model.body.paragraphs << element @model.body.append_to_element_order("p") when Wordprocessingml::Table @model.body.tables << element @model.body.append_to_element_order("tbl") when ParagraphBuilder @model.body.paragraphs << element.build @model.body.append_to_element_order("p") when TableBuilder @model.body.tables << element.build @model.body.append_to_element_order("tbl") else raise ArgumentError, "Cannot add #{element.class} to document" end self end |
#apply_styleset(name, strategy: :keep_existing) ⇒ self
Apply a bundled styleset to the document
400 401 402 403 |
# File 'lib/uniword/builder/document_builder.rb', line 400 def apply_styleset(name, strategy: :keep_existing) @model.apply_styleset(name, strategy: strategy) self end |
#author(value) ⇒ Object
365 366 367 368 |
# File 'lib/uniword/builder/document_builder.rb', line 365 def (value) @model.core_properties.creator = value self end |
#bibliography(style: "APA") {|BibliographyBuilder| ... } ⇒ BibliographyBuilder
Create and configure bibliography sources
556 557 558 559 560 561 |
# File 'lib/uniword/builder/document_builder.rb', line 556 def bibliography(style: "APA", &block) bib = BibliographyBuilder.new(style: style) block.call(bib) if block_given? bib.attach(self) bib end |
#bibliography_placeholder ⇒ self
Insert a bibliography placeholder (SDT content control)
566 567 568 569 570 571 572 |
# File 'lib/uniword/builder/document_builder.rb', line 566 def bibliography_placeholder sdt = SdtBuilder.bibliography.build para = Wordprocessingml::Paragraph.new para.sdts << sdt self << para self end |
#bookmark(name) {|ParagraphBuilder| ... } ⇒ ParagraphBuilder
Create a bookmark wrapping the next content
302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/uniword/builder/document_builder.rb', line 302 def bookmark(name, &block) id = @allocator ? @allocator.alloc_bookmark_id : begin @bookmark_counter ||= 0 @bookmark_counter += 1 @bookmark_counter.to_s end para = ParagraphBuilder.new(allocator: @allocator) para << Wordprocessingml::BookmarkStart.new(id: id, name: name) block.call(para) if block_given? para << Wordprocessingml::BookmarkEnd.new(id: id) self << para para end |
#bullet_list {|ListBuilder| ... } ⇒ ListBuilder
Shorthand: create a bullet list
293 294 295 |
# File 'lib/uniword/builder/document_builder.rb', line 293 def bullet_list(&) list(type: :bullet, &) end |
#chart(type: :bar, width: nil, height: nil) {|ChartBuilder| ... } ⇒ ChartBuilder
Insert a chart into the document
581 582 583 584 585 586 587 588 589 590 591 592 593 |
# File 'lib/uniword/builder/document_builder.rb', line 581 def chart(type: :bar, width: nil, height: nil, &block) cb = ChartBuilder.new(chart_type: type) cb.dimensions(width: width, height: height) if width || height block.call(cb) if block_given? drawing = cb.build_drawing(self) run = Wordprocessingml::Run.new run.drawings << drawing para = Wordprocessingml::Paragraph.new para.runs << run self << para cb end |
#comment(author:, text: nil, initials: nil, on: nil) {|CommentBuilder| ... } ⇒ Comment
Create a comment, register it in the document's comments collection (serialized to word/comments.xml on save), and anchor it around the most recently added body paragraph with commentRangeStart/commentRangeEnd markers and a CommentReference run. With no preceding paragraph the comment stays unanchored.
Create a comment and store it in the document's comments collection.
Without on:, the comment is anchored around the yielded
paragraph (or the document's last paragraph when no block is
given). With on:, the comment is anchored around an existing
paragraph — anywhere in the document, including table cells.
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 |
# File 'lib/uniword/builder/document_builder.rb', line 515 def comment(author:, text: nil, initials: nil, on: nil, &block) comment_id = @allocator ? @allocator.alloc_comment_id : begin @comment_counter ||= 0 @comment_counter += 1 @comment_counter.to_s end cb = CommentBuilder.new( author: , comment_id: comment_id, initials: initials ) cb << text if text block.call(cb) if block_given? comment_obj = cb.build comments_part.add_comment(comment_obj) CommentAnchorer.anchor(comment_anchor_target(on), comment_obj.comment_id) ensure_comment_reference_style comment_obj end |
#content_control(tag: nil, alias_name: nil, placeholder_text: nil) ⇒ SdtBuilder
Create a text content control paragraph
542 543 544 545 546 547 548 549 |
# File 'lib/uniword/builder/document_builder.rb', line 542 def content_control(tag: nil, alias_name: nil, placeholder_text: nil) sdt = SdtBuilder.text( tag: tag, alias_name: alias_name, placeholder_text: placeholder_text ) @model.body.paragraphs.last&.sdts&.<<(sdt.build) sdt end |
#created(value) ⇒ Object
385 386 387 388 |
# File 'lib/uniword/builder/document_builder.rb', line 385 def created(value) @model.core_properties.created = value self end |
#date_field(format: "M/d/yyyy") ⇒ self
Insert a date paragraph
615 616 617 618 |
# File 'lib/uniword/builder/document_builder.rb', line 615 def date_field(format: "M/d/yyyy") self << Builder.date_field(format: format) self end |
#define_style(name, base_on: "Normal") {|StyleBuilder| ... } ⇒ StyleBuilder
Define a paragraph style
353 354 355 356 357 358 |
# File 'lib/uniword/builder/document_builder.rb', line 353 def define_style(name, base_on: "Normal", &block) style = StyleBuilder.new(name, base_on: base_on) block.call(style) if block_given? @model.styles_configuration.add_style(style.build) style end |
#description(value) ⇒ Object
370 371 372 373 |
# File 'lib/uniword/builder/document_builder.rb', line 370 def description(value) @model.core_properties.description = value self end |
#endnote(text = nil) {|ParagraphBuilder| ... } ⇒ Wordprocessingml::Run
Create an endnote and return a Run with an endnoteReference.
331 332 333 |
# File 'lib/uniword/builder/document_builder.rb', line 331 def endnote(text = nil, &) @footnote_builder.endnote(text, &) end |
#floating_image(path, width: nil, height: nil, alt_text: nil, align: nil, vertical_align: nil, wrap: :square, behind_text: false) ⇒ self
Insert a floating image paragraph
448 449 450 451 452 453 454 455 456 457 458 |
# File 'lib/uniword/builder/document_builder.rb', line 448 def floating_image(path, width: nil, height: nil, alt_text: nil, align: nil, vertical_align: nil, wrap: :square, behind_text: false) para = Wordprocessingml::Paragraph.new para.runs << ImageBuilder.create_floating_run( self, path, width: width, height: height, alt_text: alt_text, align: align, wrap: wrap, behind_text: behind_text ) self << para self end |
#footer(type: "default") {|HeaderFooterBuilder| ... } ⇒ HeaderFooterBuilder
Configure a footer
251 252 253 254 255 256 |
# File 'lib/uniword/builder/document_builder.rb', line 251 def (type: "default", &block) hf = HeaderFooterBuilder.new(:footer, type: type, allocator: @allocator) block.call(hf) if block_given? (@model. ||= {})[type] = hf.build hf end |
#footnote(text = nil) {|ParagraphBuilder| ... } ⇒ Wordprocessingml::Run
Create a footnote and return a Run with a footnoteReference.
322 323 324 |
# File 'lib/uniword/builder/document_builder.rb', line 322 def footnote(text = nil, &) @footnote_builder.footnote(text, &) end |
#header(type: "default") {|HeaderFooterBuilder| ... } ⇒ HeaderFooterBuilder
Configure a header
239 240 241 242 243 244 |
# File 'lib/uniword/builder/document_builder.rb', line 239 def header(type: "default", &block) hf = HeaderFooterBuilder.new(:header, type: type, allocator: @allocator) block.call(hf) if block_given? (@model.headers ||= {})[type] = hf.build hf end |
#heading(text, level: 1) {|ParagraphBuilder| ... } ⇒ ParagraphBuilder
Create and add a heading paragraph
188 189 190 191 192 193 194 |
# File 'lib/uniword/builder/document_builder.rb', line 188 def heading(text, level: 1) para = ParagraphBuilder.new(allocator: @allocator) para.style = "Heading#{level}" para << text self << para para end |
#horizontal_rule(style: "single", color: "auto", size: 6) ⇒ self
Insert a horizontal rule (paragraph with bottom border)
411 412 413 414 415 416 417 418 419 |
# File 'lib/uniword/builder/document_builder.rb', line 411 def horizontal_rule(style: "single", color: "auto", size: 6) para = ParagraphBuilder.new(allocator: @allocator) para.borders( bottom: { style: style, color: color, size: size } ) para.spacing(after: 0) self << para self end |
#image(path, width: nil, height: nil, alt_text: nil) ⇒ self
Insert an inline image paragraph
428 429 430 431 432 433 434 435 |
# File 'lib/uniword/builder/document_builder.rb', line 428 def image(path, width: nil, height: nil, alt_text: nil) para = Wordprocessingml::Paragraph.new para.runs << ImageBuilder.create_run( self, path, width: width, height: height, alt_text: alt_text ) self << para self end |
#keywords(value) ⇒ Object
380 381 382 383 |
# File 'lib/uniword/builder/document_builder.rb', line 380 def keywords(value) @model.core_properties.keywords = value self end |
#list(type: :bullet) {|ListBuilder| ... } ⇒ ListBuilder
Create a list (bulleted or numbered)
275 276 277 278 279 |
# File 'lib/uniword/builder/document_builder.rb', line 275 def list(type: :bullet, &block) lb = ListBuilder.new(self, type: type) block.call(lb) if block_given? lb end |
#modified(value) ⇒ Object
390 391 392 393 |
# File 'lib/uniword/builder/document_builder.rb', line 390 def modified(value) @model.core_properties.modified = value self end |
#numbered_list {|ListBuilder| ... } ⇒ ListBuilder
Shorthand: create a numbered list
285 286 287 |
# File 'lib/uniword/builder/document_builder.rb', line 285 def numbered_list(&) list(type: :decimal, &) end |
#page_break ⇒ self
Insert a page break
199 200 201 202 203 204 |
# File 'lib/uniword/builder/document_builder.rb', line 199 def page_break self << Wordprocessingml::Paragraph.new( runs: [Builder.page_break] ) self end |
#page_number ⇒ self
Insert a page number paragraph
598 599 600 601 |
# File 'lib/uniword/builder/document_builder.rb', line 598 def page_number self << Builder.page_number_field self end |
#paragraph(text = nil) {|ParagraphBuilder| ... } ⇒ ParagraphBuilder
Create and add a paragraph to the document
174 175 176 177 178 179 180 |
# File 'lib/uniword/builder/document_builder.rb', line 174 def paragraph(text = nil, &block) para = ParagraphBuilder.new(allocator: @allocator) para << text if text block.call(para) if block_given? self << para para end |
#save(path) ⇒ Object
Save document to file
632 633 634 |
# File 'lib/uniword/builder/document_builder.rb', line 632 def save(path) @model.to_file(path) end |
#section(type: "nextPage") {|SectionBuilder| ... } ⇒ SectionBuilder
Configure section properties
222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/uniword/builder/document_builder.rb', line 222 def section(type: "nextPage", &block) sec = SectionBuilder.new sec.type = type block.call(sec) if block_given? @model.body.section_properties ||= sec.build # Register section-level headers/footers so their sectPr # references resolve to real parts at save time. sec.header_models.each { |t, hf| (@model.headers ||= {})[t] = hf } sec..each { |t, hf| (@model. ||= {})[t] = hf } sec end |
#subject(value) ⇒ Object
375 376 377 378 |
# File 'lib/uniword/builder/document_builder.rb', line 375 def subject(value) @model.core_properties.subject = value self end |
#table {|TableBuilder| ... } ⇒ TableBuilder
Create and add a table to the document
210 211 212 213 214 215 |
# File 'lib/uniword/builder/document_builder.rb', line 210 def table(&block) tbl = TableBuilder.new block.call(tbl) if block_given? self << tbl tbl end |
#theme(name = nil) {|ThemeBuilder| ... } ⇒ ThemeBuilder
Apply or configure a document theme
340 341 342 343 344 345 |
# File 'lib/uniword/builder/document_builder.rb', line 340 def theme(name = nil, &block) tb = ThemeBuilder.new(self) tb.apply(name) if name block.call(tb) if block_given? tb end |
#time_field(format: "h:mm:ss am/pm") ⇒ self
Insert a time paragraph
624 625 626 627 |
# File 'lib/uniword/builder/document_builder.rb', line 624 def time_field(format: "h:mm:ss am/pm") self << Builder.time_field(format: format) self end |
#title(value) ⇒ Object
360 361 362 363 |
# File 'lib/uniword/builder/document_builder.rb', line 360 def title(value) @model.core_properties.title = value self end |
#toc(title: "Table of Contents", styles: nil) ⇒ self
Insert a Table of Contents
263 264 265 266 267 268 |
# File 'lib/uniword/builder/document_builder.rb', line 263 def toc(title: "Table of Contents", styles: nil) TocBuilder.build(title: title, styles: styles).each do |para| self << para end self end |
#total_pages ⇒ self
Insert a total pages paragraph
606 607 608 609 |
# File 'lib/uniword/builder/document_builder.rb', line 606 def total_pages self << Builder.total_pages_field self end |
#watermark(text, font: "Calibri", size: 60, color: nil, opacity: "0.3", angle: -45)) ⇒ self
Add a watermark to the document header
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/uniword/builder/document_builder.rb', line 469 def watermark(text, font: "Calibri", size: 60, color: nil, opacity: "0.3", angle: -45) if text.nil? (@model.headers ||= {}).delete("default") return self end para = WatermarkBuilder.build_paragraph( text, font: font, size: size, color: color, opacity: opacity, angle: angle ) header = Wordprocessingml::Header.new header.paragraphs << para (@model.headers ||= {})["default"] = header self end |