uniword 1.2.4 → 1.3.0

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 (197) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +223 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/config/ooxml/schemas/shared_types.yml +5 -4
  5. data/data/schemas/ecma/dc.xsd +118 -0
  6. data/data/schemas/ecma/dcmitype.xsd +50 -0
  7. data/data/schemas/ecma/dcterms.xsd +331 -0
  8. data/data/schemas/ecma/opc-coreProperties.xsd +2 -2
  9. data/data/schemas/ecma/xml.xsd +117 -0
  10. data/lib/uniword/builder/bibliography_builder.rb +1 -1
  11. data/lib/uniword/builder/chart_builder.rb +21 -9
  12. data/lib/uniword/builder/comment_anchorer.rb +221 -0
  13. data/lib/uniword/builder/document_builder.rb +97 -6
  14. data/lib/uniword/builder/has_borders.rb +8 -1
  15. data/lib/uniword/builder/image_builder.rb +6 -5
  16. data/lib/uniword/builder/run_utils.rb +2 -0
  17. data/lib/uniword/builder.rb +1 -0
  18. data/lib/uniword/cli/fonts_cli.rb +52 -0
  19. data/lib/uniword/cli/main.rb +110 -24
  20. data/lib/uniword/cli/page_cli.rb +85 -0
  21. data/lib/uniword/cli/styles_cli.rb +204 -0
  22. data/lib/uniword/cli/theme_cli.rb +88 -0
  23. data/lib/uniword/cli/toc_cli.rb +22 -2
  24. data/lib/uniword/comment.rb +20 -15
  25. data/lib/uniword/comment_range.rb +3 -3
  26. data/lib/uniword/comments_part.rb +51 -19
  27. data/lib/uniword/configuration/configuration_loader.rb +5 -3
  28. data/lib/uniword/configuration.rb +93 -1
  29. data/lib/uniword/content_types.rb +19 -39
  30. data/lib/uniword/document_factory.rb +9 -1
  31. data/lib/uniword/document_writer.rb +16 -5
  32. data/lib/uniword/docx/chart_part.rb +34 -0
  33. data/lib/uniword/docx/custom_xml_item.rb +83 -0
  34. data/lib/uniword/docx/header_footer_part.rb +140 -0
  35. data/lib/uniword/docx/header_footer_part_collection.rb +135 -0
  36. data/lib/uniword/docx/header_footer_view.rb +225 -0
  37. data/lib/uniword/docx/id_allocator.rb +132 -38
  38. data/lib/uniword/docx/package.rb +223 -35
  39. data/lib/uniword/docx/package_defaults.rb +58 -78
  40. data/lib/uniword/docx/package_integrity_checker.rb +312 -0
  41. data/lib/uniword/docx/package_serialization.rb +116 -234
  42. data/lib/uniword/docx/part.rb +106 -0
  43. data/lib/uniword/docx/part_collection.rb +123 -0
  44. data/lib/uniword/docx/reconciler/body.rb +59 -68
  45. data/lib/uniword/docx/reconciler/fix.rb +46 -0
  46. data/lib/uniword/docx/reconciler/fix_codes.rb +37 -30
  47. data/lib/uniword/docx/reconciler/helpers.rb +9 -1
  48. data/lib/uniword/docx/reconciler/notes.rb +17 -8
  49. data/lib/uniword/docx/reconciler/package_structure.rb +169 -221
  50. data/lib/uniword/docx/reconciler/parts.rb +42 -16
  51. data/lib/uniword/docx/reconciler/referential_integrity.rb +297 -123
  52. data/lib/uniword/docx/reconciler/tables.rb +78 -16
  53. data/lib/uniword/docx/reconciler/theme.rb +7 -4
  54. data/lib/uniword/docx/reconciler.rb +40 -15
  55. data/lib/uniword/docx.rb +10 -0
  56. data/lib/uniword/drawingml/blip.rb +0 -1
  57. data/lib/uniword/drawingml/color_scheme.rb +5 -7
  58. data/lib/uniword/drawingml/font_scheme.rb +4 -8
  59. data/lib/uniword/drawingml/theme.rb +12 -7
  60. data/lib/uniword/errors.rb +8 -1
  61. data/lib/uniword/footer.rb +4 -0
  62. data/lib/uniword/header.rb +4 -0
  63. data/lib/uniword/hyperlink.rb +1 -1
  64. data/lib/uniword/image.rb +6 -6
  65. data/lib/uniword/lazy_loader.rb +1 -2
  66. data/lib/uniword/model_attribute_access.rb +20 -4
  67. data/lib/uniword/ooxml/dotx_package.rb +42 -2
  68. data/lib/uniword/ooxml/part_definition.rb +171 -0
  69. data/lib/uniword/ooxml/part_registry.rb +311 -0
  70. data/lib/uniword/ooxml/relationships/image_relationship.rb +1 -1
  71. data/lib/uniword/ooxml/relationships/package_relationships.rb +20 -22
  72. data/lib/uniword/ooxml/schema/element_serializer.rb +8 -4
  73. data/lib/uniword/ooxml/types/hex_color_value.rb +38 -0
  74. data/lib/uniword/ooxml/types/ooxml_boolean.rb +27 -14
  75. data/lib/uniword/ooxml/types/ooxml_boolean_optional.rb +21 -16
  76. data/lib/uniword/ooxml/types/theme_color_value.rb +22 -0
  77. data/lib/uniword/ooxml/types/unsigned_decimal_number.rb +30 -0
  78. data/lib/uniword/ooxml/types.rb +10 -0
  79. data/lib/uniword/ooxml.rb +2 -0
  80. data/lib/uniword/properties/alignment.rb +8 -3
  81. data/lib/uniword/properties/border.rb +52 -6
  82. data/lib/uniword/properties/cell_vertical_align.rb +4 -1
  83. data/lib/uniword/properties/color_value.rb +8 -3
  84. data/lib/uniword/properties/highlight.rb +9 -5
  85. data/lib/uniword/properties/shading.rb +4 -3
  86. data/lib/uniword/properties/tab_stop.rb +6 -1
  87. data/lib/uniword/properties/table_justification.rb +4 -1
  88. data/lib/uniword/properties/underline.rb +4 -2
  89. data/lib/uniword/properties/vertical_align.rb +6 -3
  90. data/lib/uniword/resource/theme_processor.rb +1 -1
  91. data/lib/uniword/review/review_manager.rb +23 -9
  92. data/lib/uniword/revision.rb +9 -9
  93. data/lib/uniword/schema/model_generator.rb +31 -15
  94. data/lib/uniword/shared_types/hex_color.rb +4 -1
  95. data/lib/uniword/shared_types/pixel_measure.rb +4 -1
  96. data/lib/uniword/shared_types/point_measure.rb +4 -1
  97. data/lib/uniword/shared_types/text_alignment.rb +6 -1
  98. data/lib/uniword/shared_types/twips_measure.rb +4 -1
  99. data/lib/uniword/spreadsheetml/phonetic_pr.rb +1 -1
  100. data/lib/uniword/spreadsheetml/shared_string_table.rb +2 -2
  101. data/lib/uniword/spreadsheetml/table_formula.rb +1 -1
  102. data/lib/uniword/template/variable_resolver.rb +40 -3
  103. data/lib/uniword/themes/theme_transformation.rb +56 -42
  104. data/lib/uniword/toc/toc_generator.rb +2 -0
  105. data/lib/uniword/validation/checkers/footnote_reference_checker.rb +4 -1
  106. data/lib/uniword/validation/engine.rb +35 -0
  107. data/lib/uniword/validation/link_checker.rb +1 -1
  108. data/lib/uniword/validation/opc_validator.rb +0 -1
  109. data/lib/uniword/validation/report/layer_result.rb +0 -1
  110. data/lib/uniword/validation/report/terminal_formatter.rb +0 -1
  111. data/lib/uniword/validation/report/verification_report.rb +0 -2
  112. data/lib/uniword/validation/report.rb +20 -0
  113. data/lib/uniword/validation/rules/base.rb +10 -2
  114. data/lib/uniword/validation/rules/bookmark_pairing_rule.rb +38 -0
  115. data/lib/uniword/validation/rules/bookmark_uniqueness_rule.rb +48 -0
  116. data/lib/uniword/validation/rules/bookmarks_rule.rb +0 -2
  117. data/lib/uniword/validation/rules/content_types_coverage_rule.rb +0 -2
  118. data/lib/uniword/validation/rules/core_properties_namespace_rule.rb +0 -2
  119. data/lib/uniword/validation/rules/document_body_rule.rb +25 -0
  120. data/lib/uniword/validation/rules/document_context.rb +8 -1
  121. data/lib/uniword/validation/rules/empty_paragraphs_rule.rb +27 -0
  122. data/lib/uniword/validation/rules/font_table_signature_rule.rb +0 -2
  123. data/lib/uniword/validation/rules/fonts_rule.rb +0 -2
  124. data/lib/uniword/validation/rules/footnotes_rule.rb +0 -2
  125. data/lib/uniword/validation/rules/headers_footers_rule.rb +0 -2
  126. data/lib/uniword/validation/rules/images_rule.rb +0 -2
  127. data/lib/uniword/validation/rules/mc_ignorable_namespace_rule.rb +0 -2
  128. data/lib/uniword/validation/rules/model_context.rb +32 -0
  129. data/lib/uniword/validation/rules/model_rule.rb +34 -0
  130. data/lib/uniword/validation/rules/numbering_preservation_rule.rb +0 -2
  131. data/lib/uniword/validation/rules/numbering_rule.rb +0 -2
  132. data/lib/uniword/validation/rules/relationship_integrity_rule.rb +0 -1
  133. data/lib/uniword/validation/rules/rsid_rule.rb +0 -2
  134. data/lib/uniword/validation/rules/section_properties_rule.rb +0 -2
  135. data/lib/uniword/validation/rules/settings_rule.rb +0 -2
  136. data/lib/uniword/validation/rules/settings_values_rule.rb +0 -2
  137. data/lib/uniword/validation/rules/style_references_rule.rb +0 -2
  138. data/lib/uniword/validation/rules/table_grid_rule.rb +27 -0
  139. data/lib/uniword/validation/rules/table_properties_rule.rb +27 -0
  140. data/lib/uniword/validation/rules/tables_rule.rb +0 -2
  141. data/lib/uniword/validation/rules/theme_completeness_rule.rb +0 -2
  142. data/lib/uniword/validation/rules/theme_rule.rb +0 -2
  143. data/lib/uniword/validation/rules.rb +61 -23
  144. data/lib/uniword/validation/schema_registry.rb +11 -0
  145. data/lib/uniword/validation/validators/document_semantics_validator.rb +1 -9
  146. data/lib/uniword/validation/validators/xml_schema_validator.rb +0 -2
  147. data/lib/uniword/validation/validators.rb +0 -9
  148. data/lib/uniword/validation/verify_orchestrator.rb +0 -7
  149. data/lib/uniword/validation.rb +7 -2
  150. data/lib/uniword/version.rb +1 -1
  151. data/lib/uniword/vml/imagedata.rb +0 -1
  152. data/lib/uniword/wordprocessingml/attached_template.rb +0 -1
  153. data/lib/uniword/wordprocessingml/deleted_text.rb +1 -1
  154. data/lib/uniword/wordprocessingml/document_root.rb +211 -21
  155. data/lib/uniword/wordprocessingml/endnotes.rb +4 -0
  156. data/lib/uniword/wordprocessingml/font.rb +1 -1
  157. data/lib/uniword/wordprocessingml/font_replacer.rb +147 -0
  158. data/lib/uniword/wordprocessingml/footnotes.rb +4 -0
  159. data/lib/uniword/wordprocessingml/hdr_shape_defaults.rb +0 -1
  160. data/lib/uniword/wordprocessingml/hyperlink.rb +0 -1
  161. data/lib/uniword/wordprocessingml/level.rb +6 -20
  162. data/lib/uniword/wordprocessingml/math_pr.rb +0 -11
  163. data/lib/uniword/wordprocessingml/numbering_definition.rb +16 -8
  164. data/lib/uniword/wordprocessingml/page_setup.rb +181 -0
  165. data/lib/uniword/wordprocessingml/paragraph_properties.rb +33 -28
  166. data/lib/uniword/wordprocessingml/rsids.rb +0 -2
  167. data/lib/uniword/wordprocessingml/run.rb +3 -0
  168. data/lib/uniword/wordprocessingml/run_properties.rb +20 -50
  169. data/lib/uniword/wordprocessingml/section_properties.rb +2 -2
  170. data/lib/uniword/wordprocessingml/settings.rb +8 -34
  171. data/lib/uniword/wordprocessingml/shape_defaults.rb +0 -1
  172. data/lib/uniword/wordprocessingml/style.rb +4 -3
  173. data/lib/uniword/wordprocessingml/style_cleanup.rb +198 -0
  174. data/lib/uniword/wordprocessingml/table_cell_properties.rb +6 -6
  175. data/lib/uniword/wordprocessingml/table_properties.rb +10 -15
  176. data/lib/uniword/wordprocessingml/update_fields.rb +27 -0
  177. data/lib/uniword/wordprocessingml/w14_attributes.rb +30 -2
  178. data/lib/uniword/wordprocessingml.rb +4 -0
  179. data/lib/uniword.rb +27 -2
  180. metadata +38 -19
  181. data/config/validation_rules.yml +0 -60
  182. data/config/warning_rules.yml +0 -57
  183. data/lib/uniword/validation/document_validator.rb +0 -272
  184. data/lib/uniword/validation/structural_validator.rb +0 -116
  185. data/lib/uniword/validation/validators/content_type_validator.rb +0 -165
  186. data/lib/uniword/validation/validators/file_structure_validator.rb +0 -104
  187. data/lib/uniword/validation/validators/ooxml_part_validator.rb +0 -128
  188. data/lib/uniword/validation/validators/relationship_validator.rb +0 -147
  189. data/lib/uniword/validation/validators/zip_integrity_validator.rb +0 -110
  190. data/lib/uniword/validators/element_validator.rb +0 -93
  191. data/lib/uniword/validators/paragraph_validator.rb +0 -116
  192. data/lib/uniword/validators/table_validator.rb +0 -134
  193. data/lib/uniword/validators.rb +0 -9
  194. data/lib/uniword/warnings/warning.rb +0 -130
  195. data/lib/uniword/warnings/warning_collector.rb +0 -229
  196. data/lib/uniword/warnings/warning_report.rb +0 -159
  197. data/lib/uniword/warnings.rb +0 -9
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Wordprocessingml
5
+ # Applies uniform page setup across every section of a document —
6
+ # Word's Layout dialog as an API.
7
+ #
8
+ # Paper size (named presets), orientation (with dimension swap, like
9
+ # Word), and margins (twips, or strings with in/cm/mm suffix).
10
+ # Header/footer/gutter margins are left untouched.
11
+ #
12
+ # @example A4 landscape with 2 cm margins
13
+ # setup = PageSetup.new(size: "a4", orientation: "landscape",
14
+ # margins: "2cm")
15
+ # setup.apply(document)
16
+ # setup.sections_updated # => 1
17
+ class PageSetup
18
+ # Named paper sizes in twips [width, height] (portrait).
19
+ PAPER_SIZES = {
20
+ "letter" => [12_240, 15_840],
21
+ "legal" => [12_240, 20_160],
22
+ "a4" => [11_906, 16_838],
23
+ "a5" => [8_391, 11_906],
24
+ "executive" => [10_440, 15_120],
25
+ }.freeze
26
+
27
+ TWIPS_PER_INCH = 1440
28
+ TWIPS_PER_CM = 567
29
+ TWIPS_PER_MM = 57
30
+
31
+ ORIENTATIONS = %w[portrait landscape].freeze
32
+
33
+ # @return [Integer] number of sections updated
34
+ attr_reader :sections_updated
35
+
36
+ # @param size [String, nil] Paper size name (see PAPER_SIZES)
37
+ # @param orientation [String, nil] "portrait" or "landscape"
38
+ # @param margins [Integer, String, nil] Uniform margin for all four
39
+ # sides (twips, or "1in" / "2.5cm" / "25mm")
40
+ # @param side_margins [Hash] Per-side overrides
41
+ # (:top, :right, :bottom, :left)
42
+ # @raise [ArgumentError] on unknown size, orientation, or margin
43
+ def initialize(size: nil, orientation: nil, margins: nil,
44
+ **side_margins)
45
+ @size = normalize_size(size)
46
+ @orientation = normalize_orientation(orientation)
47
+ @margins = side_margins.to_h do |side, value|
48
+ [side, parse_margin(value)]
49
+ end
50
+ uniform = parse_margin(margins)
51
+ %i[top right bottom left].each do |side|
52
+ @margins[side] = uniform if @margins[side].nil?
53
+ end
54
+ @sections_updated = 0
55
+ end
56
+
57
+ # Apply the setup to every section in the document.
58
+ #
59
+ # @param document [DocumentRoot] Document to update (mutated)
60
+ # @return [Integer] number of sections updated
61
+ def apply(document)
62
+ each_section_properties(document) do |sect_pr|
63
+ apply_page_size(sect_pr) if @size || @orientation
64
+ apply_margins(sect_pr)
65
+ @sections_updated += 1
66
+ end
67
+ sections_updated
68
+ end
69
+
70
+ private
71
+
72
+ def normalize_size(size)
73
+ return nil if size.nil?
74
+
75
+ dims = PAPER_SIZES[size.to_s.downcase]
76
+ unless dims
77
+ raise ArgumentError,
78
+ "Unknown paper size '#{size}'. " \
79
+ "Available: #{PAPER_SIZES.keys.join(', ')}"
80
+ end
81
+
82
+ dims
83
+ end
84
+
85
+ def normalize_orientation(orientation)
86
+ return nil if orientation.nil?
87
+
88
+ value = orientation.to_s.downcase
89
+ return value if ORIENTATIONS.include?(value)
90
+
91
+ raise ArgumentError,
92
+ "Unknown orientation '#{orientation}'. " \
93
+ "Available: #{ORIENTATIONS.join(', ')}"
94
+ end
95
+
96
+ # Parse a margin value: Integer (twips) or String with an
97
+ # in/cm/mm/dxa suffix (bare numbers are inches).
98
+ def parse_margin(value)
99
+ case value
100
+ when nil then nil
101
+ when Numeric then value.round
102
+ when /\A([\d.]+)\s*(in|cm|mm|dxa|twips?)?\z/i
103
+ amount = Regexp.last_match(1).to_f
104
+ unit = Regexp.last_match(2)&.downcase || "in"
105
+ to_twips(amount, unit)
106
+ else
107
+ raise ArgumentError, "Invalid margin '#{value}' " \
108
+ "(use twips, or '1in' / '2.5cm' / '25mm')"
109
+ end
110
+ end
111
+
112
+ def to_twips(amount, unit)
113
+ factor = case unit
114
+ when "in" then TWIPS_PER_INCH
115
+ when "cm" then TWIPS_PER_CM
116
+ when "mm" then TWIPS_PER_MM
117
+ else 1
118
+ end
119
+ (amount * factor).round
120
+ end
121
+
122
+ def each_section_properties(document, &block)
123
+ body = document.body
124
+ return unless body
125
+
126
+ yield body.section_properties if body.section_properties
127
+ each_paragraph_section(body, &block)
128
+ end
129
+
130
+ def each_paragraph_section(body)
131
+ (body.paragraphs || []).each do |paragraph|
132
+ sect_pr = paragraph.properties&.section_properties
133
+ yield sect_pr if sect_pr
134
+ end
135
+ end
136
+
137
+ def apply_page_size(sect_pr)
138
+ width, height = target_dimensions(sect_pr)
139
+
140
+ sect_pr.page_size ||= PageSize.new
141
+ sect_pr.page_size.width = width
142
+ sect_pr.page_size.height = height
143
+ sect_pr.page_size.orientation = @orientation if @orientation
144
+ end
145
+
146
+ def target_dimensions(sect_pr)
147
+ width, height = current_dimensions(sect_pr)
148
+ width, height = @size if @size
149
+ width, height = orient(width, height) if @orientation
150
+ [width, height]
151
+ end
152
+
153
+ def current_dimensions(sect_pr)
154
+ size = sect_pr.page_size
155
+ [size&.width || PageDefaults.default_page_size.width,
156
+ size&.height || PageDefaults.default_page_size.height]
157
+ end
158
+
159
+ # Word swaps width/height when flipping orientation.
160
+ def orient(width, height)
161
+ if (@orientation == "landscape" && width < height) ||
162
+ (@orientation == "portrait" && width > height)
163
+ [height, width]
164
+ else
165
+ [width, height]
166
+ end
167
+ end
168
+
169
+ def apply_margins(sect_pr)
170
+ return if @margins.values.all?(&:nil?)
171
+
172
+ sect_pr.page_margins ||= PageMargins.new
173
+ @margins.each do |side, value|
174
+ next if value.nil?
175
+
176
+ sect_pr.page_margins.method(:"#{side}=").call(value)
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -116,7 +116,8 @@ module Uniword
116
116
  with: { from: :yaml_widow_control_from, to: :yaml_widow_control_to }
117
117
  end
118
118
 
119
- # YAML transform methods (instance methods - called via send on an instance)
119
+ # YAML transform methods (instance methods called by lutaml-model's
120
+ # `with:` transform mechanism)
120
121
  def yaml_style_from(instance, value)
121
122
  if value
122
123
  instance.style = [
@@ -200,21 +201,6 @@ module Uniword
200
201
  # Style reference - maps w:pStyle w:val="..." to style attribute
201
202
  map_element "pStyle", to: :style, render_nil: false
202
203
 
203
- # Alignment - maps w:jc w:val="..." to alignment attribute
204
- map_element "jc", to: :alignment, render_nil: false
205
-
206
- # Spacing (complex object - WORKS)
207
- map_element "spacing", to: :spacing, render_nil: false
208
-
209
- # Indentation (complex object)
210
- map_element "ind", to: :indentation, render_nil: false
211
-
212
- # Outline level (wrapper object)
213
- map_element "outlineLvl", to: :outline_level, render_nil: false
214
-
215
- # Numbering properties (wrapped in w:numPr)
216
- map_element "numPr", to: :numbering_properties, render_nil: false
217
-
218
204
  # Keep options (only render if true)
219
205
  map_element "keepNext", to: :keep_next_wrapper, render_nil: false,
220
206
  render_default: false
@@ -225,32 +211,51 @@ module Uniword
225
211
  map_element "widowControl", to: :widow_control_wrapper, render_nil: false,
226
212
  render_default: false
227
213
 
228
- # Spacing options (only render if true)
229
- map_element "contextualSpacing", to: :contextual_spacing, render_nil: false,
230
- render_default: false
214
+ # Numbering properties (wrapped in w:numPr)
215
+ map_element "numPr", to: :numbering_properties, render_nil: false
216
+
217
+ # Suppress line numbers (only render if true)
231
218
  map_element "suppressLineNumbers", to: :suppress_line_numbers, render_nil: false,
232
219
  render_default: false
233
220
 
234
- # Bidirectional (only render if true)
235
- map_element "bidi", to: :bidirectional, render_nil: false,
236
- render_default: false
221
+ # Borders (complex object)
222
+ map_element "pBdr", to: :borders, render_nil: false
223
+
224
+ # Shading (complex object)
225
+ map_element "shd", to: :shading, render_nil: false
226
+
227
+ # Tabs (complex object - collection)
228
+ map_element "tabs", to: :tabs, render_nil: false
237
229
 
238
230
  # East Asian typography
239
231
  map_element "autoSpaceDE", to: :auto_space_de, render_nil: false,
240
232
  render_default: false
241
233
  map_element "autoSpaceDN", to: :auto_space_dn, render_nil: false,
242
234
  render_default: false
235
+
236
+ # Bidirectional (only render if true)
237
+ map_element "bidi", to: :bidirectional, render_nil: false,
238
+ render_default: false
239
+
240
+ # Right indent adjustment
243
241
  map_element "adjustRightInd", to: :adjust_right_ind, render_nil: false,
244
242
  render_default: false
245
243
 
246
- # Borders (complex object)
247
- map_element "pBdr", to: :borders, render_nil: false
244
+ # Spacing (complex object - WORKS)
245
+ map_element "spacing", to: :spacing, render_nil: false
248
246
 
249
- # Tabs (complex object - collection)
250
- map_element "tabs", to: :tabs, render_nil: false
247
+ # Indentation (complex object)
248
+ map_element "ind", to: :indentation, render_nil: false
251
249
 
252
- # Shading (complex object)
253
- map_element "shd", to: :shading, render_nil: false
250
+ # Contextual spacing (only render if true)
251
+ map_element "contextualSpacing", to: :contextual_spacing, render_nil: false,
252
+ render_default: false
253
+
254
+ # Alignment - maps w:jc w:val="..." to alignment attribute
255
+ map_element "jc", to: :alignment, render_nil: false
256
+
257
+ # Outline level (wrapper object)
258
+ map_element "outlineLvl", to: :outline_level, render_nil: false
254
259
 
255
260
  # Run properties (for pPr/rPr - style overrides)
256
261
  map_element "rPr", to: :run_properties, render_nil: false
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "rsid_root"
5
- require_relative "rsid"
6
4
 
7
5
  module Uniword
8
6
  module Wordprocessingml
@@ -18,6 +18,7 @@ module Uniword
18
18
  attribute :alternate_content, AlternateContent, default: nil
19
19
  attribute :footnote_reference, FootnoteReference
20
20
  attribute :endnote_reference, EndnoteReference
21
+ attribute :comment_reference, CommentReference
21
22
  attribute :field_char, FieldChar
22
23
  attribute :instr_text, InstrText
23
24
  attribute :position_tab, PositionTab
@@ -55,6 +56,8 @@ module Uniword
55
56
  render_nil: false
56
57
  map_element "endnoteReference", to: :endnote_reference,
57
58
  render_nil: false
59
+ map_element "commentReference", to: :comment_reference,
60
+ render_nil: false
58
61
  map_element "fldChar", to: :field_char, render_nil: false
59
62
  map_element "instrText", to: :instr_text, render_nil: false
60
63
  map_element "ptab", to: :position_tab, render_nil: false
@@ -121,73 +121,43 @@ module Uniword
121
121
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
122
122
  mixed_content
123
123
 
124
- # Style reference (wrapper object)
124
+ # ECMA-376 CT_RPr schema sequence order:
125
+ # rStyle, rFonts, b, bCs, i, iCs, caps, smallCaps, strike, dstrike,
126
+ # outline, shadow, emboss, imprint, noProof, snapToGrid, vanish, webHidden,
127
+ # color, spacing, w, kern, position, sz, szCs, highlight, u, effect, bdr,
128
+ # shd, fitText, vertAlign, rtl, cs, em, lang, eastAsianLayout, specVanish, oMath
125
129
  map_element "rStyle", to: :style, render_nil: false
126
-
127
- # Fonts (complex object)
128
130
  map_element "rFonts", to: :fonts, render_nil: false
129
-
130
- # Font sizes (wrapper objects)
131
- map_element "sz", to: :size, render_nil: false
132
- map_element "szCs", to: :size_cs, render_nil: false
133
-
134
- # Boolean formatting (wrapper objects)
135
131
  map_element "b", to: :bold, render_nil: false
136
132
  map_element "bCs", to: :bold_cs, render_nil: false
137
133
  map_element "i", to: :italic, render_nil: false
138
134
  map_element "iCs", to: :italic_cs, render_nil: false
135
+ map_element "caps", to: :caps, render_nil: false
136
+ map_element "smallCaps", to: :small_caps, render_nil: false
139
137
  map_element "strike", to: :strike, render_nil: false
140
138
  map_element "dstrike", to: :double_strike, render_nil: false
141
- map_element "smallCaps", to: :small_caps, render_nil: false
142
- map_element "caps", to: :caps, render_nil: false
143
- map_element "vanish", to: :hidden, render_nil: false
139
+ map_element "outline", to: :outline, render_nil: false
140
+ map_element "shadow", to: :shadow, render_nil: false
141
+ map_element "emboss", to: :emboss, render_nil: false
142
+ map_element "imprint", to: :imprint, render_nil: false
144
143
  map_element "noProof", to: :no_proof, render_nil: false
144
+ map_element "vanish", to: :hidden, render_nil: false
145
145
  map_element "webHidden", to: :web_hidden, render_nil: false
146
-
147
- # Color (wrapper object)
148
146
  map_element "color", to: :color, render_nil: false
149
-
150
- # Underline (wrapper object)
151
- map_element "u", to: :underline, render_nil: false
152
-
153
- # Highlight (wrapper object)
154
- map_element "highlight", to: :highlight, render_nil: false
155
-
156
- # Vertical alignment (wrapper object)
157
- map_element "vertAlign", to: :vertical_align, render_nil: false
158
-
159
- # Position (wrapper object)
160
- map_element "position", to: :position, render_nil: false
161
-
162
- # Character spacing (wrapper object)
163
147
  map_element "spacing", to: :character_spacing, render_nil: false
164
-
165
- # Kerning (wrapper object)
166
- map_element "kern", to: :kerning, render_nil: false
167
-
168
- # Width scale (wrapper object)
169
148
  map_element "w", to: :width_scale, render_nil: false
170
-
171
- # Emphasis mark (wrapper object)
172
- map_element "em", to: :emphasis_mark, render_nil: false
173
-
174
- # Shading (complex object)
149
+ map_element "kern", to: :kerning, render_nil: false
150
+ map_element "position", to: :position, render_nil: false
151
+ map_element "sz", to: :size, render_nil: false
152
+ map_element "szCs", to: :size_cs, render_nil: false
153
+ map_element "highlight", to: :highlight, render_nil: false
154
+ map_element "u", to: :underline, render_nil: false
175
155
  map_element "shd", to: :shading, render_nil: false
176
-
177
- # Language (complex object)
156
+ map_element "vertAlign", to: :vertical_align, render_nil: false
157
+ map_element "em", to: :emphasis_mark, render_nil: false
178
158
  map_element "lang", to: :language, render_nil: false
179
-
180
- # Text effects (complex objects - basic support)
181
159
  map_element "textFill", to: :text_fill, render_nil: false
182
160
  map_element "textOutline", to: :text_outline, render_nil: false
183
-
184
- # Additional boolean formatting
185
- map_element "shadow", to: :shadow, render_nil: false
186
- map_element "emboss", to: :emboss, render_nil: false
187
- map_element "imprint", to: :imprint, render_nil: false
188
- map_element "outline", to: :outline, render_nil: false
189
-
190
- # W14 namespace elements
191
161
  map_element "ligatures", to: :ligatures, render_nil: false
192
162
  end
193
163
  end
@@ -50,14 +50,14 @@ module Uniword
50
50
 
51
51
  map_element "headerReference", to: :header_references, render_nil: false
52
52
  map_element "footerReference", to: :footer_references, render_nil: false
53
+ map_element "footnotePr", to: :footnote_pr, render_nil: false
53
54
  map_element "pgSz", to: :page_size, render_nil: false
54
55
  map_element "pgMar", to: :page_margins, render_nil: false
55
56
  map_attribute "val", to: :type
56
57
  map_element "pgNumType", to: :page_numbering, render_nil: false
57
58
  map_element "cols", to: :columns, render_nil: false
58
- map_element "docGrid", to: :doc_grid, render_nil: false
59
- map_element "footnotePr", to: :footnote_pr, render_nil: false
60
59
  map_element "titlePg", to: :title_pg, render_nil: false
60
+ map_element "docGrid", to: :doc_grid, render_nil: false
61
61
  map_element "footnoteColumns", to: :footnote_columns, render_nil: false
62
62
  end
63
63
  end
@@ -2,34 +2,6 @@
2
2
 
3
3
  require "lutaml/model"
4
4
  require "nokogiri"
5
- require_relative "../properties/relationship_id"
6
- require_relative "zoom"
7
- require_relative "compat"
8
- require_relative "proof_state"
9
- require_relative "style_pane_format_filter"
10
- require_relative "default_tab_stop"
11
- require_relative "character_spacing_control"
12
- require_relative "do_not_display_page_boundaries"
13
- require_relative "rsids"
14
- require_relative "math_pr"
15
- require_relative "theme_font_lang"
16
- require_relative "clr_scheme_mapping"
17
- require_relative "shape_defaults"
18
- require_relative "decimal_symbol"
19
- require_relative "list_separator"
20
- require_relative "attached_template"
21
- require_relative "footnote_pr"
22
- require_relative "endnote_pr"
23
- require_relative "hdr_shape_defaults"
24
- require_relative "even_and_odd_headers"
25
- require_relative "mirror_margins"
26
- require_relative "do_not_include_subdocs_in_stats"
27
- require_relative "hyphenation_zone"
28
- require_relative "style_pane_sort_method"
29
- require_relative "doc_vars"
30
- require_relative "w14_doc_id"
31
- require_relative "w15_chart_tracking_ref_based"
32
- require_relative "w15_doc_id"
33
5
 
34
6
  module Uniword
35
7
  module Wordprocessingml
@@ -57,6 +29,7 @@ module Uniword
57
29
  attribute :footnote_pr, FootnotePr
58
30
  attribute :endnote_pr, EndnotePr
59
31
  attribute :hdr_shape_defaults, HdrShapeDefaults
32
+ attribute :update_fields, UpdateFields
60
33
  attribute :even_and_odd_headers, EvenAndOddHeaders
61
34
  attribute :mirror_margins, MirrorMargins
62
35
  attribute :do_not_include_subdocs_in_stats,
@@ -112,9 +85,9 @@ module Uniword
112
85
  map_element "doNotDisplayPageBoundaries",
113
86
  to: :do_not_display_page_boundaries, render_nil: false
114
87
  map_element "mirrorMargins", to: :mirror_margins, render_nil: false
88
+ map_element "proofState", to: :proof_state, render_nil: false
115
89
  map_element "attachedTemplate", to: :attached_template,
116
90
  render_nil: false
117
- map_element "proofState", to: :proof_state, render_nil: false
118
91
  map_element "stylePaneFormatFilter", to: :style_pane_format_filter,
119
92
  render_nil: false
120
93
  map_element "stylePaneSortMethod", to: :style_pane_sort_method,
@@ -126,6 +99,9 @@ module Uniword
126
99
  render_nil: false
127
100
  map_element "characterSpacingControl", to: :character_spacing_control,
128
101
  render_nil: false
102
+ map_element "updateFields", to: :update_fields, render_nil: false
103
+ map_element "hdrShapeDefaults", to: :hdr_shape_defaults,
104
+ render_nil: false
129
105
  map_element "footnotePr", to: :footnote_pr, render_nil: false
130
106
  map_element "endnotePr", to: :endnote_pr, render_nil: false
131
107
  map_element "compat", to: :compat, render_nil: false
@@ -137,11 +113,6 @@ module Uniword
137
113
  render_nil: false
138
114
  map_element "doNotIncludeSubdocsInStats",
139
115
  to: :do_not_include_subdocs_in_stats, render_nil: false
140
- map_element "hdrShapeDefaults", to: :hdr_shape_defaults,
141
- render_nil: false
142
- map_element "shapeDefaults", to: :shape_defaults, render_nil: false
143
- map_element "decimalSymbol", to: :decimal_symbol, render_nil: false
144
- map_element "listSeparator", to: :list_separator, render_nil: false
145
116
  # Both w14:docId and w15:docId use the same element name 'docId'
146
117
  # Separate map_element entries needed for namespace-aware matching
147
118
  # The namespace_scope ensures w14 and w15 namespaces are declared on root
@@ -150,6 +121,9 @@ module Uniword
150
121
  map_element "docId", to: :w14_doc_id, render_nil: false
151
122
  map_element "docId", to: :w15_doc_id, render_nil: false
152
123
  map_element "schemaLibrary", to: :schema_library, render_nil: false
124
+ map_element "shapeDefaults", to: :shape_defaults, render_nil: false
125
+ map_element "decimalSymbol", to: :decimal_symbol, render_nil: false
126
+ map_element "listSeparator", to: :list_separator, render_nil: false
153
127
  end
154
128
 
155
129
  # Override from_xml to manually deserialize w15:docId which has the same
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "../vml_office"
5
4
 
6
5
  module Uniword
7
6
  module Wordprocessingml
@@ -49,7 +49,8 @@ module Uniword
49
49
  map "run_properties", to: :rPr
50
50
  end
51
51
 
52
- # YAML transform methods (instance methods - called via send on an instance)
52
+ # YAML transform methods (instance methods called by lutaml-model's
53
+ # `with:` transform mechanism)
53
54
  def yaml_name_from(instance, value)
54
55
  instance.name = StyleName.new(val: value) if value
55
56
  end
@@ -111,12 +112,12 @@ module Uniword
111
112
  value_map: { to: { true => true, false => :omitted } }
112
113
  map_element "name", to: :name, render_nil: false
113
114
  map_element "basedOn", to: :basedOn, render_nil: false
114
- map_element "next", to: :nextStyle, render_nil: false # Maps XML 'next' to nextStyle attribute
115
+ map_element "next", to: :nextStyle, render_nil: false
115
116
  map_element "link", to: :link, render_nil: false
116
117
  map_element "uiPriority", to: :uiPriority, render_nil: false
117
- map_element "qFormat", to: :qFormat, render_nil: false
118
118
  map_element "semiHidden", to: :semiHidden, render_nil: false
119
119
  map_element "unhideWhenUsed", to: :unhideWhenUsed, render_nil: false
120
+ map_element "qFormat", to: :qFormat, render_nil: false
120
121
  map_element "rsid", to: :rsid, render_nil: false
121
122
  map_element "pPr", to: :pPr, render_nil: false
122
123
  map_element "rPr", to: :rPr, render_nil: false