Files
Notare/lib/notare/document.rb
mathias234 843466549a
All checks were successful
CI Pipeline / build (pull_request) Successful in 13s
Support table and table column sizing
2025-12-03 13:50:56 +01:00

127 lines
3.4 KiB
Ruby

# frozen_string_literal: true
module Notare
class Document
include Builder
attr_reader :nodes, :styles, :table_styles, :hyperlinks
def self.create(path, &block)
doc = new
block.call(doc)
doc.save(path)
doc
end
def initialize
@nodes = []
@format_stack = []
@current_target = nil
@current_list = nil
@current_table = nil
@current_row = nil
@num_id_counter = 0
@has_lists = false
@images = {}
@hyperlinks = []
@styles = {}
@table_styles = {}
register_built_in_styles
register_built_in_table_styles
end
def define_style(name, **properties)
@styles[name] = Style.new(name, **properties)
end
def style(name)
@styles[name]
end
def define_table_style(name, **properties)
@table_styles[name] = TableStyle.new(name, **properties)
end
def table_style(name)
@table_styles[name]
end
def save(path)
Package.new(self).save(path)
end
def lists
@nodes.select { |n| n.is_a?(Nodes::List) }
end
def uses_lists?
@has_lists
end
def mark_has_lists!
@has_lists = true
end
def images
@images.values
end
def register_hyperlink(url)
rid = next_hyperlink_rid
hyperlink = Nodes::Hyperlink.new(url: url, rid: rid)
@hyperlinks << hyperlink
hyperlink
end
def register_image(path, width: nil, height: nil)
return @images[path] if @images[path]
rid = next_image_rid
width_emu, height_emu = ImageDimensions.calculate_emus(path, width: width, height: height)
image = Nodes::Image.new(path, rid: rid, width_emu: width_emu, height_emu: height_emu)
@images[path] = image
image
end
private
def next_image_rid
# rId1 = styles.xml (always present)
# rId2 = numbering.xml (if lists present)
# rId3+ = images and hyperlinks share the same ID space
base = @has_lists ? 3 : 2
"rId#{base + @images.size + @hyperlinks.size}"
end
def next_hyperlink_rid
# Images and hyperlinks share the same ID space
base = @has_lists ? 3 : 2
"rId#{base + @images.size + @hyperlinks.size}"
end
def register_built_in_styles
# Headings (spacing_before ensures they're rendered as paragraph styles)
define_style :heading1, size: 24, bold: true, spacing_before: 240, spacing_after: 120
define_style :heading2, size: 18, bold: true, spacing_before: 200, spacing_after: 100
define_style :heading3, size: 14, bold: true, spacing_before: 160, spacing_after: 80
define_style :heading4, size: 12, bold: true, spacing_before: 120, spacing_after: 60
define_style :heading5, size: 11, bold: true, italic: true, spacing_before: 100, spacing_after: 40
define_style :heading6, size: 10, bold: true, italic: true, spacing_before: 80, spacing_after: 40
# Other built-in styles
define_style :title, size: 26, bold: true, align: :center
define_style :subtitle, size: 15, italic: true, color: "666666"
define_style :quote, italic: true, color: "666666", indent: 720
define_style :code, font: "Courier New", size: 10
end
def register_built_in_table_styles
define_table_style :grid,
borders: { style: "single", color: "000000", size: 4 }
define_table_style :borderless,
borders: :none
end
end
end