# 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, then hyperlinks base = @has_lists ? 3 : 2 "rId#{base + @images.size}" end def next_hyperlink_rid # Hyperlinks come after images 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