Files
Notare/lib/notare/xml/styles_xml.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

154 lines
5.3 KiB
Ruby

# frozen_string_literal: true
module Notare
module Xml
class StylesXml
NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
ALIGNMENT_MAP = {
left: "left",
center: "center",
right: "right",
justify: "both"
}.freeze
TABLE_ALIGNMENT_MAP = {
left: "left",
center: "center",
right: "right"
}.freeze
def initialize(styles, table_styles = {})
@styles = styles
@table_styles = table_styles
end
def to_xml
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
xml.styles("xmlns:w" => NAMESPACE) do
xml.parent.namespace = xml.parent.namespace_definitions.find { |ns| ns.prefix == "w" }
@styles.each_value do |style|
render_style(xml, style)
end
render_table_normal_style(xml) if @table_styles.any?
@table_styles.each_value do |style|
render_table_style(xml, style)
end
end
end
builder.to_xml
end
private
def render_style(xml, style)
style_type = style.paragraph_properties? ? "paragraph" : "character"
xml["w"].style("w:type" => style_type, "w:styleId" => style.style_id) do
xml["w"].name("w:val" => style.display_name)
render_paragraph_properties(xml, style) if style.paragraph_properties?
render_run_properties(xml, style) if style.text_properties?
end
end
def render_paragraph_properties(xml, style)
xml["w"].pPr do
xml["w"].jc("w:val" => ALIGNMENT_MAP[style.align]) if style.align
xml["w"].ind("w:left" => style.indent.to_s) if style.indent
if style.spacing_before || style.spacing_after
spacing_attrs = {}
spacing_attrs["w:before"] = style.spacing_before.to_s if style.spacing_before
spacing_attrs["w:after"] = style.spacing_after.to_s if style.spacing_after
xml["w"].spacing(spacing_attrs)
end
end
end
def render_run_properties(xml, style)
xml["w"].rPr do
xml["w"].rFonts("w:ascii" => style.font, "w:hAnsi" => style.font) if style.font
xml["w"].sz("w:val" => style.size_half_points.to_s) if style.size
xml["w"].color("w:val" => style.color) if style.color
xml["w"].b if style.bold
xml["w"].i if style.italic
xml["w"].u("w:val" => "single") if style.underline
xml["w"].strike if style.strike
xml["w"].highlight("w:val" => style.highlight) if style.highlight
end
end
def render_table_normal_style(xml)
xml["w"].style("w:type" => "table", "w:default" => "1", "w:styleId" => "TableNormal") do
xml["w"].name("w:val" => "Normal Table")
xml["w"].tblPr do
xml["w"].tblCellMar do
xml["w"].top("w:w" => "0", "w:type" => "dxa")
xml["w"].left("w:w" => "108", "w:type" => "dxa")
xml["w"].bottom("w:w" => "0", "w:type" => "dxa")
xml["w"].right("w:w" => "108", "w:type" => "dxa")
end
end
end
end
def render_table_style(xml, style)
xml["w"].style("w:type" => "table", "w:styleId" => style.style_id) do
xml["w"].name("w:val" => style.display_name)
xml["w"].basedOn("w:val" => "TableNormal")
xml["w"].tblPr do
render_table_borders(xml, style.borders) if style.borders
render_table_shading(xml, style.shading) if style.shading
render_table_cell_margins(xml, style.cell_margins) if style.cell_margins
xml["w"].jc("w:val" => TABLE_ALIGNMENT_MAP[style.align]) if style.align
end
end
end
def render_table_borders(xml, borders)
xml["w"].tblBorders do
%i[top left bottom right insideH insideV].each do |pos|
border = borders == :none ? :none : (borders[pos] || borders)
render_single_border(xml, pos, border)
end
end
end
def render_single_border(xml, position, border)
if border == :none
xml["w"].send(position, "w:val" => "nil")
else
xml["w"].send(position,
"w:val" => border[:style],
"w:sz" => border[:size].to_s,
"w:space" => "0",
"w:color" => border[:color])
end
end
def render_table_shading(xml, color)
xml["w"].shd("w:val" => "clear", "w:color" => "auto", "w:fill" => color)
end
def render_table_cell_margins(xml, margins)
xml["w"].tblCellMar do
if margins.is_a?(Hash)
xml["w"].top("w:w" => margins[:top].to_s, "w:type" => "dxa") if margins[:top]
xml["w"].left("w:w" => margins[:left].to_s, "w:type" => "dxa") if margins[:left]
xml["w"].bottom("w:w" => margins[:bottom].to_s, "w:type" => "dxa") if margins[:bottom]
xml["w"].right("w:w" => margins[:right].to_s, "w:type" => "dxa") if margins[:right]
else
%i[top left bottom right].each do |side|
xml["w"].send(side, "w:w" => margins.to_s, "w:type" => "dxa")
end
end
end
end
end
end
end