# frozen_string_literal: true module Ezdoc module Builder def p(text = nil, &block) para = Nodes::Paragraph.new if block with_target(para, &block) elsif text para.add_run(Nodes::Run.new(text, **current_formatting)) end @nodes << para end def text(value) @current_target.add_run(Nodes::Run.new(value, **current_formatting)) end def image(path, width: nil, height: nil) validate_image_path!(path) img = register_image(path, width: width, height: height) @current_target.add_run(img) end def b(&block) with_format(:bold, &block) end def i(&block) with_format(:italic, &block) end def u(&block) with_format(:underline, &block) end def ul(&block) list(:bullet, &block) end def ol(&block) list(:number, &block) end def li(text = nil, &block) item = Nodes::ListItem.new([], list_type: @current_list.type, num_id: @current_list.num_id) if block with_target(item, &block) elsif text item.add_run(Nodes::Run.new(text, **current_formatting)) end @current_list.add_item(item) end def table(&block) tbl = Nodes::Table.new previous_table = @current_table @current_table = tbl block.call @current_table = previous_table @nodes << tbl end def tr(&block) row = Nodes::TableRow.new previous_row = @current_row @current_row = row block.call @current_row = previous_row @current_table.add_row(row) end def td(text = nil, &block) cell = Nodes::TableCell.new if block with_target(cell, &block) elsif text cell.add_run(Nodes::Run.new(text, **current_formatting)) end @current_row.add_cell(cell) end private def list(type, &block) @num_id_counter ||= 0 @num_id_counter += 1 list_node = Nodes::List.new(type: type, num_id: @num_id_counter) previous_list = @current_list @current_list = list_node block.call @current_list = previous_list @nodes << list_node end def with_format(format, &block) @format_stack ||= [] @format_stack.push(format) block.call @format_stack.pop end def with_target(target, &block) previous_target = @current_target @current_target = target block.call @current_target = previous_target end def current_formatting @format_stack ||= [] { bold: @format_stack.include?(:bold), italic: @format_stack.include?(:italic), underline: @format_stack.include?(:underline) } end def validate_image_path!(path) raise ArgumentError, "Image file not found: #{path}" unless File.exist?(path) ext = File.extname(path).downcase return if %w[.png .jpg .jpeg].include?(ext) raise ArgumentError, "Unsupported image format: #{ext}. Use PNG or JPEG." end end end