uniword 1.0.11 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/config/font_metadata.yml +56 -0
  3. data/lib/uniword/accessibility/rules/descriptive_headings_rule.rb +1 -1
  4. data/lib/uniword/assembly/toc.rb +4 -14
  5. data/lib/uniword/assembly/variable_substitutor.rb +3 -4
  6. data/lib/uniword/batch/stages/normalize_styles_stage.rb +4 -2
  7. data/lib/uniword/builder/base_builder.rb +25 -0
  8. data/lib/uniword/builder/chart_builder.rb +33 -14
  9. data/lib/uniword/builder/deterministic_id.rb +17 -0
  10. data/lib/uniword/builder/document_builder.rb +129 -34
  11. data/lib/uniword/builder/footnote_builder.rb +22 -23
  12. data/lib/uniword/builder/header_footer_builder.rb +10 -10
  13. data/lib/uniword/builder/image_builder.rb +24 -16
  14. data/lib/uniword/builder/list_builder.rb +2 -1
  15. data/lib/uniword/builder/paragraph_builder.rb +95 -20
  16. data/lib/uniword/builder/run_utils.rb +97 -0
  17. data/lib/uniword/builder/section_builder.rb +40 -67
  18. data/lib/uniword/builder/table_builder.rb +32 -13
  19. data/lib/uniword/builder/table_cell_builder.rb +19 -0
  20. data/lib/uniword/builder.rb +27 -19
  21. data/lib/uniword/cli/diff_cli.rb +0 -1
  22. data/lib/uniword/cli/generate_cli.rb +0 -1
  23. data/lib/uniword/cli/headers_cli.rb +0 -1
  24. data/lib/uniword/cli/images_cli.rb +0 -1
  25. data/lib/uniword/cli/main.rb +6 -7
  26. data/lib/uniword/cli/protect_cli.rb +0 -1
  27. data/lib/uniword/cli/resources_cli.rb +0 -1
  28. data/lib/uniword/cli/review_cli.rb +0 -1
  29. data/lib/uniword/cli/spellcheck_cli.rb +0 -1
  30. data/lib/uniword/cli/styleset_cli.rb +0 -1
  31. data/lib/uniword/cli/template_cli.rb +0 -1
  32. data/lib/uniword/cli/theme_cli.rb +0 -1
  33. data/lib/uniword/cli/toc_cli.rb +0 -1
  34. data/lib/uniword/cli/watermark_cli.rb +0 -1
  35. data/lib/uniword/cli.rb +6 -17
  36. data/lib/uniword/diff/document_differ.rb +23 -5
  37. data/lib/uniword/diff/package_differ.rb +0 -1
  38. data/lib/uniword/diff.rb +4 -0
  39. data/lib/uniword/document_factory.rb +16 -25
  40. data/lib/uniword/docx/document_statistics.rb +42 -54
  41. data/lib/uniword/docx/id_allocator.rb +143 -0
  42. data/lib/uniword/docx/package.rb +42 -6
  43. data/lib/uniword/docx/package_defaults.rb +15 -25
  44. data/lib/uniword/docx/package_serialization.rb +131 -127
  45. data/lib/uniword/docx/reconciler/body.rb +197 -0
  46. data/lib/uniword/docx/reconciler/fix_codes.rb +74 -0
  47. data/lib/uniword/docx/reconciler/helpers.rb +337 -0
  48. data/lib/uniword/docx/reconciler/notes.rb +301 -0
  49. data/lib/uniword/docx/reconciler/package_structure.rb +322 -0
  50. data/lib/uniword/docx/reconciler/parts.rb +484 -0
  51. data/lib/uniword/docx/reconciler/referential_integrity.rb +449 -0
  52. data/lib/uniword/docx/reconciler/tables.rb +162 -0
  53. data/lib/uniword/docx/reconciler/theme.rb +141 -0
  54. data/lib/uniword/docx/reconciler.rb +44 -1097
  55. data/lib/uniword/docx.rb +3 -0
  56. data/lib/uniword/drawingml/color_scheme.rb +38 -5
  57. data/lib/uniword/hyperlink.rb +27 -26
  58. data/lib/uniword/infrastructure/zip_packager.rb +41 -38
  59. data/lib/uniword/lazy_loader.rb +36 -62
  60. data/lib/uniword/math.rb +3 -86
  61. data/lib/uniword/mhtml/document.rb +1 -3
  62. data/lib/uniword/model_attribute_access.rb +27 -0
  63. data/lib/uniword/ooxml/namespaces.rb +62 -2
  64. data/lib/uniword/ooxml/relationships/package_relationships.rb +7 -0
  65. data/lib/uniword/presentationml/slide_id.rb +0 -1
  66. data/lib/uniword/presentationml/slide_master_id.rb +0 -1
  67. data/lib/uniword/properties/bold.rb +0 -2
  68. data/lib/uniword/properties/boolean_formatting.rb +0 -2
  69. data/lib/uniword/properties/borders.rb +1 -1
  70. data/lib/uniword/properties/italic.rb +0 -2
  71. data/lib/uniword/properties/numbering_properties.rb +1 -1
  72. data/lib/uniword/properties/style_reference.rb +13 -0
  73. data/lib/uniword/quality/rules/heading_hierarchy_rule.rb +1 -1
  74. data/lib/uniword/quality/rules/style_consistency_rule.rb +1 -1
  75. data/lib/uniword/review/review_manager.rb +0 -2
  76. data/lib/uniword/template/helpers/loop_helper.rb +2 -2
  77. data/lib/uniword/template/variable_resolver.rb +14 -1
  78. data/lib/uniword/theme/theme_xml_parser.rb +1 -1
  79. data/lib/uniword/toc/toc_generator.rb +3 -14
  80. data/lib/uniword/transformation/mhtml_element_renderer.rb +94 -37
  81. data/lib/uniword/transformation/mhtml_metadata_builder.rb +1 -1
  82. data/lib/uniword/transformation/ooxml_to_html_converter.rb +3 -5
  83. data/lib/uniword/validation/checkers/internal_link_checker.rb +2 -3
  84. data/lib/uniword/version.rb +1 -1
  85. data/lib/uniword/visitor/text_extractor.rb +2 -1
  86. data/lib/uniword/watermark/manager.rb +2 -2
  87. data/lib/uniword/wordprocessingml/allow_png.rb +14 -0
  88. data/lib/uniword/wordprocessingml/body.rb +51 -34
  89. data/lib/uniword/wordprocessingml/body_div.rb +17 -0
  90. data/lib/uniword/wordprocessingml/div_border.rb +23 -0
  91. data/lib/uniword/wordprocessingml/div_borders.rb +24 -0
  92. data/lib/uniword/wordprocessingml/divs_child.rb +17 -0
  93. data/lib/uniword/wordprocessingml/document_root.rb +4 -3
  94. data/lib/uniword/wordprocessingml/endnote.rb +1 -1
  95. data/lib/uniword/wordprocessingml/endnotes.rb +4 -0
  96. data/lib/uniword/wordprocessingml/footer.rb +4 -0
  97. data/lib/uniword/wordprocessingml/footer_reference.rb +0 -1
  98. data/lib/uniword/wordprocessingml/footnote.rb +1 -1
  99. data/lib/uniword/wordprocessingml/footnotes.rb +4 -0
  100. data/lib/uniword/wordprocessingml/header.rb +4 -0
  101. data/lib/uniword/wordprocessingml/header_reference.rb +0 -1
  102. data/lib/uniword/wordprocessingml/hyperlink.rb +2 -2
  103. data/lib/uniword/wordprocessingml/instr_text.rb +20 -5
  104. data/lib/uniword/wordprocessingml/level.rb +2 -0
  105. data/lib/uniword/wordprocessingml/mar_bottom.rb +17 -0
  106. data/lib/uniword/wordprocessingml/mar_left.rb +17 -0
  107. data/lib/uniword/wordprocessingml/mar_right.rb +17 -0
  108. data/lib/uniword/wordprocessingml/mar_top.rb +17 -0
  109. data/lib/uniword/wordprocessingml/num.rb +4 -0
  110. data/lib/uniword/wordprocessingml/numbering.rb +1 -22
  111. data/lib/uniword/wordprocessingml/optimize_for_browser.rb +14 -0
  112. data/lib/uniword/wordprocessingml/page_defaults.rb +39 -0
  113. data/lib/uniword/wordprocessingml/paragraph.rb +16 -13
  114. data/lib/uniword/wordprocessingml/paragraph_properties.rb +12 -8
  115. data/lib/uniword/wordprocessingml/run.rb +32 -36
  116. data/lib/uniword/wordprocessingml/run_properties/merging.rb +45 -0
  117. data/lib/uniword/wordprocessingml/run_properties.rb +8 -5
  118. data/lib/uniword/wordprocessingml/section_properties.rb +3 -3
  119. data/lib/uniword/wordprocessingml/separator_char.rb +21 -0
  120. data/lib/uniword/wordprocessingml/settings.rb +19 -17
  121. data/lib/uniword/wordprocessingml/styles/style_definition.rb +1 -1
  122. data/lib/uniword/wordprocessingml/styles/style_library.rb +16 -0
  123. data/lib/uniword/wordprocessingml/table_defaults.rb +44 -0
  124. data/lib/uniword/wordprocessingml/text.rb +8 -2
  125. data/lib/uniword/wordprocessingml/web_div.rb +41 -0
  126. data/lib/uniword/wordprocessingml/web_divs.rb +18 -0
  127. data/lib/uniword/wordprocessingml/web_encoding.rb +17 -0
  128. data/lib/uniword/wordprocessingml/web_settings.rb +5 -21
  129. data/lib/uniword/wordprocessingml/zoom.rb +2 -0
  130. data/lib/uniword/wordprocessingml.rb +23 -0
  131. data/lib/uniword.rb +27 -0
  132. metadata +46 -70
  133. data/lib/uniword/math/accent.rb +0 -25
  134. data/lib/uniword/math/accent_properties.rb +0 -25
  135. data/lib/uniword/math/argument_properties.rb +0 -22
  136. data/lib/uniword/math/bar.rb +0 -25
  137. data/lib/uniword/math/bar_properties.rb +0 -25
  138. data/lib/uniword/math/begin_char.rb +0 -22
  139. data/lib/uniword/math/border_box.rb +0 -25
  140. data/lib/uniword/math/border_box_properties.rb +0 -39
  141. data/lib/uniword/math/box.rb +0 -25
  142. data/lib/uniword/math/box_properties.rb +0 -33
  143. data/lib/uniword/math/char.rb +0 -22
  144. data/lib/uniword/math/control_properties.rb +0 -27
  145. data/lib/uniword/math/degree.rb +0 -26
  146. data/lib/uniword/math/delimiter.rb +0 -25
  147. data/lib/uniword/math/delimiter_properties.rb +0 -33
  148. data/lib/uniword/math/denominator.rb +0 -26
  149. data/lib/uniword/math/element.rb +0 -61
  150. data/lib/uniword/math/end_char.rb +0 -22
  151. data/lib/uniword/math/equation_array.rb +0 -25
  152. data/lib/uniword/math/equation_array_properties.rb +0 -33
  153. data/lib/uniword/math/fraction.rb +0 -27
  154. data/lib/uniword/math/fraction_properties.rb +0 -25
  155. data/lib/uniword/math/function.rb +0 -27
  156. data/lib/uniword/math/function_name.rb +0 -26
  157. data/lib/uniword/math/function_properties.rb +0 -23
  158. data/lib/uniword/math/group_char.rb +0 -25
  159. data/lib/uniword/math/group_char_properties.rb +0 -29
  160. data/lib/uniword/math/lim.rb +0 -26
  161. data/lib/uniword/math/lower_limit.rb +0 -27
  162. data/lib/uniword/math/lower_limit_properties.rb +0 -23
  163. data/lib/uniword/math/math_break.rb +0 -22
  164. data/lib/uniword/math/math_font.rb +0 -22
  165. data/lib/uniword/math/math_properties.rb +0 -53
  166. data/lib/uniword/math/math_run.rb +0 -42
  167. data/lib/uniword/math/math_run_properties.rb +0 -37
  168. data/lib/uniword/math/math_simple_int_val.rb +0 -24
  169. data/lib/uniword/math/math_simple_val.rb +0 -24
  170. data/lib/uniword/math/math_style.rb +0 -22
  171. data/lib/uniword/math/math_text.rb +0 -23
  172. data/lib/uniword/math/matrix.rb +0 -25
  173. data/lib/uniword/math/matrix_column.rb +0 -23
  174. data/lib/uniword/math/matrix_column_properties.rb +0 -24
  175. data/lib/uniword/math/matrix_columns.rb +0 -23
  176. data/lib/uniword/math/matrix_properties.rb +0 -37
  177. data/lib/uniword/math/matrix_row.rb +0 -23
  178. data/lib/uniword/math/nary.rb +0 -29
  179. data/lib/uniword/math/nary_properties.rb +0 -33
  180. data/lib/uniword/math/numerator.rb +0 -26
  181. data/lib/uniword/math/o_math.rb +0 -65
  182. data/lib/uniword/math/o_math_para.rb +0 -25
  183. data/lib/uniword/math/o_math_para_properties.rb +0 -22
  184. data/lib/uniword/math/phantom.rb +0 -25
  185. data/lib/uniword/math/phantom_properties.rb +0 -33
  186. data/lib/uniword/math/pre_sub_superscript.rb +0 -29
  187. data/lib/uniword/math/pre_sub_superscript_properties.rb +0 -23
  188. data/lib/uniword/math/radical.rb +0 -27
  189. data/lib/uniword/math/radical_properties.rb +0 -25
  190. data/lib/uniword/math/separator_char.rb +0 -22
  191. data/lib/uniword/math/sub.rb +0 -26
  192. data/lib/uniword/math/sub_superscript.rb +0 -29
  193. data/lib/uniword/math/sub_superscript_properties.rb +0 -25
  194. data/lib/uniword/math/subscript.rb +0 -27
  195. data/lib/uniword/math/subscript_properties.rb +0 -23
  196. data/lib/uniword/math/sup.rb +0 -26
  197. data/lib/uniword/math/superscript.rb +0 -27
  198. data/lib/uniword/math/superscript_properties.rb +0 -23
  199. data/lib/uniword/math/upper_limit.rb +0 -27
  200. data/lib/uniword/math/upper_limit_properties.rb +0 -23
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ class DivBorders < Lutaml::Model::Serializable
8
+ attribute :top, DivBorder
9
+ attribute :left, DivBorder
10
+ attribute :bottom, DivBorder
11
+ attribute :right, DivBorder
12
+
13
+ xml do
14
+ element "divBdr"
15
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
16
+ mixed_content
17
+ map_element "top", to: :top, render_nil: false
18
+ map_element "left", to: :left, render_nil: false
19
+ map_element "bottom", to: :bottom, render_nil: false
20
+ map_element "right", to: :right, render_nil: false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ # Defined as a stub first to break the WebDiv ↔ DivsChild mutual recursion.
8
+ # The :div attribute is added after WebDiv is defined.
9
+ class DivsChild < Lutaml::Model::Serializable
10
+ xml do
11
+ element "divsChild"
12
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
13
+ mixed_content
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,9 +2,6 @@
2
2
 
3
3
  require "lutaml/model"
4
4
 
5
- require_relative "../document_input"
6
- require_relative "document_root/feature_facade"
7
-
8
5
  module Uniword
9
6
  module Wordprocessingml
10
7
  # Root element of a WordprocessingML document
@@ -12,6 +9,8 @@ module Uniword
12
9
  # Generated from OOXML schema: wordprocessingml.yml
13
10
  # Element: <w:document>
14
11
  class DocumentRoot < Lutaml::Model::Serializable
12
+ autoload :FeatureFacade, "#{__dir__}/document_root/feature_facade"
13
+
15
14
  include FeatureFacade
16
15
  include Uniword::DocumentInput
17
16
 
@@ -117,6 +116,8 @@ module Uniword
117
116
  # Round-trip parts (copied from DocxPackage during load)
118
117
  attr_accessor :settings, :font_table, :web_settings, :document_rels, :theme_rels,
119
118
  :package_rels, :content_types, :custom_properties, :custom_xml_items
119
+ # Central ID allocator — preserves IDs across build/save cycle
120
+ attr_accessor :allocator
120
121
 
121
122
  # Writers for properties that have lazy-initialized getters
122
123
  # (removing from attr_accessor to avoid shadowing custom getters)
@@ -19,7 +19,7 @@ module Uniword
19
19
  mixed_content
20
20
 
21
21
  map_attribute "id", to: :id
22
- map_attribute "type", to: :type
22
+ map_attribute "type", to: :type, render_nil: false
23
23
  map_element "p", to: :paragraphs, render_nil: false
24
24
  end
25
25
  end
@@ -9,6 +9,7 @@ module Uniword
9
9
  # Generated from OOXML schema: wordprocessingml.yml
10
10
  # Element: <w:endnotes>
11
11
  class Endnotes < Lutaml::Model::Serializable
12
+ attribute :mc_ignorable, Uniword::Ooxml::Types::McIgnorable
12
13
  attribute :endnote_entries, Endnote, collection: true,
13
14
  initialize_empty: true
14
15
 
@@ -17,6 +18,9 @@ module Uniword
17
18
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
18
19
  mixed_content
19
20
 
21
+ namespace_scope Uniword::Ooxml::Namespaces::DOCUMENT_PART_SCOPES
22
+
23
+ map_attribute "Ignorable", to: :mc_ignorable, render_nil: false
20
24
  map_element "endnote", to: :endnote_entries, render_nil: false
21
25
  end
22
26
 
@@ -9,6 +9,7 @@ module Uniword
9
9
  # Generated from OOXML schema: wordprocessingml.yml
10
10
  # Element: <w:ftr>
11
11
  class Footer < Lutaml::Model::Serializable
12
+ attribute :mc_ignorable, Uniword::Ooxml::Types::McIgnorable
12
13
  attribute :paragraphs, Paragraph, collection: true, initialize_empty: true
13
14
  attribute :tables, Table, collection: true, initialize_empty: true
14
15
 
@@ -17,6 +18,9 @@ module Uniword
17
18
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
18
19
  mixed_content
19
20
 
21
+ namespace_scope Uniword::Ooxml::Namespaces::DOCUMENT_PART_SCOPES
22
+
23
+ map_attribute "Ignorable", to: :mc_ignorable, render_nil: false
20
24
  map_element "p", to: :paragraphs, render_nil: false
21
25
  map_element "tbl", to: :tables, render_nil: false
22
26
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "../properties/relationship_id"
5
4
 
6
5
  module Uniword
7
6
  module Wordprocessingml
@@ -19,7 +19,7 @@ module Uniword
19
19
  mixed_content
20
20
 
21
21
  map_attribute "id", to: :id
22
- map_attribute "type", to: :type
22
+ map_attribute "type", to: :type, render_nil: false
23
23
  map_element "p", to: :paragraphs, render_nil: false
24
24
  end
25
25
  end
@@ -9,6 +9,7 @@ module Uniword
9
9
  # Generated from OOXML schema: wordprocessingml.yml
10
10
  # Element: <w:footnotes>
11
11
  class Footnotes < Lutaml::Model::Serializable
12
+ attribute :mc_ignorable, Uniword::Ooxml::Types::McIgnorable
12
13
  attribute :footnote_entries, Footnote, collection: true,
13
14
  initialize_empty: true
14
15
 
@@ -17,6 +18,9 @@ module Uniword
17
18
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
18
19
  mixed_content
19
20
 
21
+ namespace_scope Uniword::Ooxml::Namespaces::DOCUMENT_PART_SCOPES
22
+
23
+ map_attribute "Ignorable", to: :mc_ignorable, render_nil: false
20
24
  map_element "footnote", to: :footnote_entries, render_nil: false
21
25
  end
22
26
 
@@ -9,6 +9,7 @@ module Uniword
9
9
  # Generated from OOXML schema: wordprocessingml.yml
10
10
  # Element: <w:hdr>
11
11
  class Header < Lutaml::Model::Serializable
12
+ attribute :mc_ignorable, Uniword::Ooxml::Types::McIgnorable
12
13
  attribute :paragraphs, Paragraph, collection: true, initialize_empty: true
13
14
  attribute :tables, Table, collection: true, initialize_empty: true
14
15
 
@@ -17,6 +18,9 @@ module Uniword
17
18
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
18
19
  mixed_content
19
20
 
21
+ namespace_scope Uniword::Ooxml::Namespaces::DOCUMENT_PART_SCOPES
22
+
23
+ map_attribute "Ignorable", to: :mc_ignorable, render_nil: false
20
24
  map_element "p", to: :paragraphs, render_nil: false
21
25
  map_element "tbl", to: :tables, render_nil: false
22
26
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "../properties/relationship_id"
5
4
 
6
5
  module Uniword
7
6
  module Wordprocessingml
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "../has_run_position"
4
+ require "uniword/ooxml/types/relationship_id"
5
5
 
6
6
  module Uniword
7
7
  module Wordprocessingml
@@ -12,7 +12,7 @@ module Uniword
12
12
  class Hyperlink < Lutaml::Model::Serializable
13
13
  include Uniword::HasRunPosition
14
14
 
15
- attribute :id, :string
15
+ attribute :id, Ooxml::Types::RelationshipId
16
16
  attribute :anchor, :string
17
17
  attribute :tooltip, :string
18
18
  attribute :history, Uniword::Properties::HistoryValue
@@ -9,7 +9,7 @@ module Uniword
9
9
  # Generated from OOXML schema: wordprocessingml.yml
10
10
  # Element: <w:instrText>
11
11
  class InstrText < Lutaml::Model::Serializable
12
- attribute :text, :string
12
+ attribute :content, :string, collection: true, initialize_empty: true
13
13
  attribute :xml_space, :string
14
14
 
15
15
  xml do
@@ -17,17 +17,32 @@ module Uniword
17
17
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
18
18
  mixed_content
19
19
 
20
+ map_content to: :content
20
21
  map_attribute "xml:space", to: :xml_space
21
- map_element "text", to: :text, render_nil: false
22
22
  end
23
23
 
24
24
  def initialize(attrs = {})
25
- if attrs[:text].is_a?(String) && (attrs[:text].start_with?(" ") || attrs[:text].end_with?(" ") || attrs[:text].include?("\t"))
26
- attrs[:xml_space] =
27
- "preserve"
25
+ if attrs.is_a?(Hash)
26
+ text_val = attrs.delete(:text) || attrs[:content]
27
+ if text_val.is_a?(String)
28
+ attrs[:xml_space] = "preserve" if Text.preserve_whitespace?(text_val)
29
+ attrs[:content] = [text_val] unless attrs.key?(:content)
30
+ end
28
31
  end
29
32
  super
30
33
  end
34
+
35
+ def text
36
+ content&.join
37
+ end
38
+
39
+ def text=(value)
40
+ self.content = value.is_a?(Array) ? value : [value.to_s]
41
+ end
42
+
43
+ def to_s
44
+ text.to_s
45
+ end
31
46
  end
32
47
  end
33
48
  end
@@ -66,6 +66,7 @@ module Uniword
66
66
  class Level < Lutaml::Model::Serializable
67
67
  attribute :ilvl, :integer
68
68
  attribute :tentative, :string
69
+ attribute :tplc, :string
69
70
  attribute :start, Start
70
71
  attribute :numFmt, NumFmt
71
72
  attribute :pStyle, PStyle
@@ -85,6 +86,7 @@ module Uniword
85
86
 
86
87
  map_attribute "ilvl", to: :ilvl
87
88
  map_attribute "tentative", to: :tentative, render_nil: false
89
+ map_attribute "tplc", to: :tplc, render_nil: false
88
90
  map_element "start", to: :start, render_nil: false
89
91
  map_element "numFmt", to: :numFmt, render_nil: false
90
92
  map_element "pStyle", to: :pStyle, render_nil: false
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ class MarBottom < Lutaml::Model::Serializable
8
+ attribute :val, :string
9
+
10
+ xml do
11
+ element "marBottom"
12
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
13
+ map_attribute "val", to: :val
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ class MarLeft < Lutaml::Model::Serializable
8
+ attribute :val, :string
9
+
10
+ xml do
11
+ element "marLeft"
12
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
13
+ map_attribute "val", to: :val
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ class MarRight < Lutaml::Model::Serializable
8
+ attribute :val, :string
9
+
10
+ xml do
11
+ element "marRight"
12
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
13
+ map_attribute "val", to: :val
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ class MarTop < Lutaml::Model::Serializable
8
+ attribute :val, :string
9
+
10
+ xml do
11
+ element "marTop"
12
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
13
+ map_attribute "val", to: :val
14
+ end
15
+ end
16
+ end
17
+ end
@@ -10,6 +10,7 @@ module Uniword
10
10
  # Element: <w:num>
11
11
  class Num < Lutaml::Model::Serializable
12
12
  attribute :numId, :integer
13
+ attribute :durable_id, :string
13
14
  attribute :abstractNumId, AbstractNumId
14
15
  attribute :lvlOverrides, LevelOverride, collection: true,
15
16
  initialize_empty: true
@@ -20,6 +21,9 @@ module Uniword
20
21
  mixed_content
21
22
 
22
23
  map_attribute "numId", to: :numId
24
+ map_attribute "durableId", to: :durable_id,
25
+ namespace: Uniword::Ooxml::Namespaces::Word2016Cid,
26
+ render_nil: false
23
27
  map_element "abstractNumId", to: :abstractNumId, render_nil: false
24
28
  map_element "lvlOverride", to: :lvlOverrides, render_nil: false
25
29
  end
@@ -18,28 +18,7 @@ module Uniword
18
18
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
19
19
  mixed_content
20
20
 
21
- # Force namespace declarations on root for mc:Ignorable prefixes
22
- namespace_scope [
23
- { namespace: Uniword::Ooxml::Namespaces::MarkupCompatibility,
24
- declare: :always },
25
- { namespace: Uniword::Ooxml::Namespaces::Word2010, declare: :always },
26
- { namespace: Uniword::Ooxml::Namespaces::Word2012, declare: :always },
27
- { namespace: Uniword::Ooxml::Namespaces::Word2015Symex,
28
- declare: :always },
29
- { namespace: Uniword::Ooxml::Namespaces::Word2016Cid,
30
- declare: :always },
31
- { namespace: Uniword::Ooxml::Namespaces::Word2018, declare: :always },
32
- { namespace: Uniword::Ooxml::Namespaces::Word2018Cex,
33
- declare: :always },
34
- { namespace: Uniword::Ooxml::Namespaces::Word2020SdtDataHash,
35
- declare: :always },
36
- { namespace: Uniword::Ooxml::Namespaces::Word2024SdtFormatLock,
37
- declare: :always },
38
- { namespace: Uniword::Ooxml::Namespaces::Word2023Du,
39
- declare: :always },
40
- { namespace: Uniword::Ooxml::Namespaces::Word2010Drawing,
41
- declare: :always },
42
- ]
21
+ namespace_scope Uniword::Ooxml::Namespaces::DOCUMENT_PART_SCOPES
43
22
 
44
23
  map_element "abstractNum", to: :abstractNums, render_nil: false
45
24
  map_element "num", to: :nums, render_nil: false
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ class OptimizeForBrowser < Lutaml::Model::Serializable
8
+ xml do
9
+ element "optimizeForBrowser"
10
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Wordprocessingml
5
+ # Shared page layout defaults.
6
+ #
7
+ # Provides canonical values for page size, margins, columns, and docGrid
8
+ # used by both builder and reconciler.
9
+ module PageDefaults
10
+ LETTER_WIDTH = 12_240
11
+ LETTER_HEIGHT = 15_840
12
+
13
+ DEFAULT_MARGINS = {
14
+ top: 1440, right: 1440, bottom: 1440, left: 1440,
15
+ header: 720, footer: 720, gutter: 0,
16
+ }.freeze
17
+
18
+ DEFAULT_COL_SPACE = 720
19
+ DEFAULT_LINE_PITCH = 360
20
+
21
+ def self.default_page_size
22
+ Wordprocessingml::PageSize.new(width: LETTER_WIDTH,
23
+ height: LETTER_HEIGHT)
24
+ end
25
+
26
+ def self.default_page_margins
27
+ Wordprocessingml::PageMargins.new(**DEFAULT_MARGINS)
28
+ end
29
+
30
+ def self.default_columns
31
+ Wordprocessingml::Columns.new(space: DEFAULT_COL_SPACE)
32
+ end
33
+
34
+ def self.default_doc_grid
35
+ Wordprocessingml::DocGrid.new(line_pitch: DEFAULT_LINE_PITCH)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
+ require "omml"
4
5
 
5
6
  module Uniword
6
7
  module Wordprocessingml
@@ -21,8 +22,11 @@ module Uniword
21
22
  attribute :comment_references, CommentReference, collection: true, initialize_empty: true
22
23
  attribute :alternate_content, AlternateContent, default: nil
23
24
  attribute :sdts, StructuredDocumentTag, collection: true, initialize_empty: true
24
- attribute :o_math_paras, Uniword::Math::OMathPara, collection: true, initialize_empty: true
25
- attribute :o_maths, Uniword::Math::OMath, collection: true, initialize_empty: true
25
+ # OMML math content from the omml gem. The element name (`<m:oMath>`)
26
+ # is supplied by the parent mapping; CTOMath is the schema type that
27
+ # carries the actual math tree.
28
+ attribute :o_math_paras, "Omml::Models::OMathPara", collection: true, initialize_empty: true
29
+ attribute :o_maths, "Omml::Models::CTOMath", collection: true, initialize_empty: true
26
30
  attribute :proof_errors, ProofErr, collection: true, initialize_empty: true
27
31
  attribute :simple_fields, SimpleField, collection: true, initialize_empty: true
28
32
 
@@ -45,15 +49,15 @@ module Uniword
45
49
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
46
50
  mixed_content
47
51
 
52
+ # W14 namespace attributes first (matches Word output order)
53
+ map_attribute "paraId", to: :para_id, render_nil: false
54
+ map_attribute "textId", to: :text_id, render_nil: false
48
55
  # Revision tracking attributes
49
56
  map_attribute "rsidR", to: :rsid_r, render_nil: false
50
57
  map_attribute "rsidRDefault", to: :rsid_r_default, render_nil: false
51
58
  map_attribute "rsidP", to: :rsid_p, render_nil: false
52
59
  map_attribute "rsidRPr", to: :rsid_r_pr, render_nil: false
53
60
  map_attribute "rsidDel", to: :rsid_del, render_nil: false
54
- # W14 namespace typed attributes - namespace declared on the type class
55
- map_attribute "paraId", to: :para_id, render_nil: false
56
- map_attribute "textId", to: :text_id, render_nil: false
57
61
 
58
62
  map_element "pPr", to: :properties, render_nil: false
59
63
  map_element "r", to: :runs, render_nil: false
@@ -68,11 +72,10 @@ module Uniword
68
72
  map_element "AlternateContent", to: :alternate_content, render_nil: false
69
73
  map_element "sdt", to: :sdts, render_nil: false
70
74
  # oMathPara from MathML namespace - the target class declares its namespace
71
- map_element "oMathPara", to: :o_math_paras,
72
- render_nil: false
73
- # Inline oMath (without oMathPara wrapper)
74
- map_element "oMath", to: :o_maths,
75
- render_nil: false
75
+ map_element "oMathPara", to: :o_math_paras, render_nil: false
76
+ # Inline oMath (without oMathPara wrapper). The element name "oMath"
77
+ # comes from this mapping; the underlying type is omml gem's CTOMath.
78
+ map_element "oMath", to: :o_maths, render_nil: false
76
79
  # Proofing errors
77
80
  map_element "proofErr", to: :proof_errors, render_nil: false
78
81
  map_element "fldSimple", to: :simple_fields, render_nil: false
@@ -92,7 +95,7 @@ module Uniword
92
95
  if run_or_sdt.is_a?(StructuredDocumentTag)
93
96
  extract_sdt_text(run_or_sdt)
94
97
  else
95
- run_or_sdt.text.to_s
98
+ run_or_sdt.text_string
96
99
  end
97
100
  end
98
101
 
@@ -100,7 +103,7 @@ module Uniword
100
103
  def extract_sdt_text(sdt)
101
104
  return "" unless sdt.content
102
105
 
103
- sdt.content.runs.map { |r| r.text.to_s }.join
106
+ sdt.content.runs.map(&:text_string).join
104
107
  end
105
108
 
106
109
  def empty?
@@ -108,7 +111,7 @@ module Uniword
108
111
  end
109
112
 
110
113
  def style
111
- properties&.style
114
+ Array(properties&.style).first
112
115
  end
113
116
 
114
117
  def alignment
@@ -11,9 +11,12 @@ module Uniword
11
11
  class ParagraphProperties < Lutaml::Model::Serializable
12
12
  # Pattern 0: ATTRIBUTES FIRST, then XML mappings
13
13
 
14
- # Simple element attributes (OOXML w:val attributes stored in wrapper objects)
15
- attribute :style, Properties::StyleReference # w:pStyle w:val="..." (style ID reference)
16
- attribute :alignment, Properties::Alignment # w:jc w:val="..." (alignment value)
14
+ # Simple element attributes (OOXML w:val attributes stored in
15
+ # wrapper objects)
16
+ # w:pStyle w:val="..." (style ID references; multiple tolerated)
17
+ attribute :style, Properties::StyleReference, collection: true
18
+ # w:jc w:val="..." (alignment value)
19
+ attribute :alignment, Properties::Alignment
17
20
 
18
21
  # Simple element wrapper objects
19
22
  attribute :outline_level, Properties::OutlineLevel
@@ -115,11 +118,15 @@ module Uniword
115
118
 
116
119
  # YAML transform methods (instance methods - called via send on an instance)
117
120
  def yaml_style_from(instance, value)
118
- instance.style = Properties::StyleReference.new(value: value) if value
121
+ if value
122
+ instance.style = [
123
+ Properties::StyleReference.new(value: value),
124
+ ]
125
+ end
119
126
  end
120
127
 
121
128
  def yaml_style_to(instance, _doc)
122
- instance.style&.value
129
+ Array(instance.style).first&.value
123
130
  end
124
131
 
125
132
  def yaml_alignment_from(instance, value)
@@ -288,9 +295,6 @@ module Uniword
288
295
  # This handles cases like ParagraphProperties.new(spacing_before: 120)
289
296
  # where the flat attribute is set but the wrapper object is not
290
297
  def convert_flat_attributes!
291
- # Style - convert string to StyleReference wrapper
292
- self.style = Properties::StyleReference.new(value: @style) if @style.is_a?(String)
293
-
294
298
  # Alignment - convert string to Alignment wrapper
295
299
  self.alignment = Properties::Alignment.new(value: @alignment) if @alignment.is_a?(String)
296
300