uniword 1.7.1 → 1.7.2

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/lib/uniword/accessibility/rules/image_alt_text_rule.rb +36 -13
  4. data/lib/uniword/builder/image_builder.rb +2 -0
  5. data/lib/uniword/builder/paragraph_builder.rb +5 -6
  6. data/lib/uniword/builder/sdt_builder.rb +12 -4
  7. data/lib/uniword/builder/style_builder.rb +5 -5
  8. data/lib/uniword/builder/table_builder.rb +2 -2
  9. data/lib/uniword/builder/table_cell_builder.rb +3 -3
  10. data/lib/uniword/builder/table_row_builder.rb +2 -1
  11. data/lib/uniword/cli/comments_cli.rb +173 -0
  12. data/lib/uniword/cli/diff_cli.rb +60 -0
  13. data/lib/uniword/cli/main.rb +104 -0
  14. data/lib/uniword/cli/plain_text_command.rb +101 -0
  15. data/lib/uniword/cli/review_cli.rb +173 -34
  16. data/lib/uniword/cli/revisions_cli.rb +88 -0
  17. data/lib/uniword/cli/template_cli.rb +81 -0
  18. data/lib/uniword/converter/plain_text.rb +426 -0
  19. data/lib/uniword/converter/plain_text_result.rb +112 -0
  20. data/lib/uniword/converter.rb +11 -0
  21. data/lib/uniword/diff/document_differ.rb +2 -88
  22. data/lib/uniword/diff/lcs.rb +101 -0
  23. data/lib/uniword/diff/redline_result.rb +94 -0
  24. data/lib/uniword/diff/redliner.rb +319 -0
  25. data/lib/uniword/diff.rb +3 -0
  26. data/lib/uniword/errors.rb +33 -0
  27. data/lib/uniword/mhtml/word_css.rb +16 -8
  28. data/lib/uniword/ooxml/types/ooxml_boolean.rb +19 -7
  29. data/lib/uniword/ooxml/types/ooxml_boolean_optional.rb +4 -8
  30. data/lib/uniword/properties/boolean_element_factory.rb +48 -3
  31. data/lib/uniword/properties/boolean_formatting.rb +4 -0
  32. data/lib/uniword/properties/outline.rb +4 -13
  33. data/lib/uniword/quality/rules/image_alt_text_rule.rb +22 -43
  34. data/lib/uniword/review/review_manager.rb +128 -1
  35. data/lib/uniword/template/fill_result.rb +121 -0
  36. data/lib/uniword/template/filler.rb +292 -0
  37. data/lib/uniword/template.rb +2 -0
  38. data/lib/uniword/transformation/mhtml_element_renderer.rb +9 -3
  39. data/lib/uniword/transformation/ooxml_to_html_converter.rb +8 -3
  40. data/lib/uniword/version.rb +1 -1
  41. data/lib/uniword/wordprocessingml/deleted_paragraph_mark.rb +32 -0
  42. data/lib/uniword/wordprocessingml/document_root.rb +40 -6
  43. data/lib/uniword/wordprocessingml/drawing.rb +51 -0
  44. data/lib/uniword/wordprocessingml/inserted_paragraph_mark.rb +32 -0
  45. data/lib/uniword/wordprocessingml/paragraph_properties.rb +128 -34
  46. data/lib/uniword/wordprocessingml/run_properties/conversion.rb +30 -9
  47. data/lib/uniword/wordprocessingml/run_properties/predicates.rb +25 -50
  48. data/lib/uniword/wordprocessingml/run_properties/yaml_transforms.rb +61 -44
  49. data/lib/uniword/wordprocessingml/run_properties.rb +10 -0
  50. data/lib/uniword/wordprocessingml/structured_document_tag/showing_placeholder_header.rb +13 -2
  51. data/lib/uniword/wordprocessingml/structured_document_tag/temporary.rb +13 -2
  52. data/lib/uniword/wordprocessingml/style.rb +43 -40
  53. data/lib/uniword/wordprocessingml/table_row_properties.rb +16 -12
  54. data/lib/uniword/wordprocessingml/yaml_writer.rb +24 -0
  55. data/lib/uniword/wordprocessingml.rb +17 -4
  56. data/lib/uniword.rb +9 -0
  57. metadata +20 -6
@@ -8,63 +8,73 @@ module Uniword
8
8
  # Handles bidirectional conversion between flat YAML keys
9
9
  # (bold, italic, size, etc.) and the wrapper objects used
10
10
  # internally by the OOXML model.
11
+ #
12
+ # Every writer assigns through YamlWriter#yaml_put; see that module
13
+ # for why.
11
14
  module YamlTransforms
15
+ include YamlWriter
16
+
12
17
  # --- Boolean transforms ---
18
+ #
19
+ # Toggles read through BooleanElement#on?, the same reading the XML
20
+ # and predicate consumers get.
13
21
 
14
22
  def yaml_bold_from(instance, value)
15
23
  instance.bold = Properties::Bold.new(value: value) unless value.nil?
16
24
  end
17
25
 
18
- def yaml_bold_to(_instance, _doc)
19
- bold&.value
26
+ def yaml_bold_to(instance, doc)
27
+ yaml_put(doc, "bold", instance.bold&.on?)
20
28
  end
21
29
 
22
30
  def yaml_italic_from(instance, value)
23
31
  instance.italic = Properties::Italic.new(value: value) unless value.nil?
24
32
  end
25
33
 
26
- def yaml_italic_to(_instance, _doc)
27
- italic&.value
34
+ def yaml_italic_to(instance, doc)
35
+ yaml_put(doc, "italic", instance.italic&.on?)
28
36
  end
29
37
 
30
38
  def yaml_strike_from(instance, value)
31
39
  instance.strike = Properties::Strike.new(value: value) unless value.nil?
32
40
  end
33
41
 
34
- def yaml_strike_to(_instance, _doc)
35
- strike&.value
42
+ def yaml_strike_to(instance, doc)
43
+ yaml_put(doc, "strike", instance.strike&.on?)
36
44
  end
37
45
 
38
46
  def yaml_double_strike_from(instance, value)
39
47
  instance.double_strike = Properties::DoubleStrike.new(value: value) unless value.nil?
40
48
  end
41
49
 
42
- def yaml_double_strike_to(_instance, _doc)
43
- double_strike&.value
50
+ def yaml_double_strike_to(instance, doc)
51
+ yaml_put(doc, "double_strike", instance.double_strike&.on?)
44
52
  end
45
53
 
46
54
  def yaml_small_caps_from(instance, value)
47
55
  instance.small_caps = Properties::SmallCaps.new(value: value) unless value.nil?
48
56
  end
49
57
 
50
- def yaml_small_caps_to(_instance, _doc)
51
- small_caps&.value
58
+ def yaml_small_caps_to(instance, doc)
59
+ yaml_put(doc, "small_caps", instance.small_caps&.on?)
52
60
  end
53
61
 
54
62
  def yaml_caps_from(instance, value)
55
63
  instance.caps = Properties::Caps.new(value: value) unless value.nil?
56
64
  end
57
65
 
58
- def yaml_caps_to(_instance, _doc)
59
- caps&.value
66
+ # "caps" and "all_caps" are two spellings of one property, so both
67
+ # rules write the canonical key rather than emitting it twice.
68
+ def yaml_caps_to(instance, doc)
69
+ yaml_put(doc, "caps", instance.caps&.on?)
60
70
  end
61
71
 
62
72
  def yaml_hidden_from(instance, value)
63
73
  instance.hidden = Properties::Vanish.new(value: value) unless value.nil?
64
74
  end
65
75
 
66
- def yaml_hidden_to(_instance, _doc)
67
- hidden&.value
76
+ def yaml_hidden_to(instance, doc)
77
+ yaml_put(doc, "hidden", instance.hidden&.on?)
68
78
  end
69
79
 
70
80
  # --- Numeric transforms ---
@@ -73,16 +83,16 @@ module Uniword
73
83
  instance.size = Properties::FontSize.new(value: value.to_i) if value
74
84
  end
75
85
 
76
- def yaml_size_to(_instance, _doc)
77
- size&.value
86
+ def yaml_size_to(instance, doc)
87
+ yaml_put(doc, "size", instance.size&.value)
78
88
  end
79
89
 
80
90
  def yaml_character_spacing_from(instance, value)
81
91
  instance.character_spacing = Properties::CharacterSpacing.new(value: value.to_i) if value
82
92
  end
83
93
 
84
- def yaml_character_spacing_to(_instance, _doc)
85
- character_spacing&.value
94
+ def yaml_character_spacing_to(instance, doc)
95
+ yaml_put(doc, "character_spacing", instance.character_spacing&.value)
86
96
  end
87
97
 
88
98
  # --- String transforms ---
@@ -91,24 +101,24 @@ module Uniword
91
101
  instance.underline = Properties::Underline.new(value: value) if value
92
102
  end
93
103
 
94
- def yaml_underline_to(_instance, _doc)
95
- underline&.value
104
+ def yaml_underline_to(instance, doc)
105
+ yaml_put(doc, "underline", instance.underline&.value)
96
106
  end
97
107
 
98
108
  def yaml_color_from(instance, value)
99
109
  instance.color = Properties::ColorValue.new(value: value) if value
100
110
  end
101
111
 
102
- def yaml_color_to(_instance, _doc)
103
- color&.value
112
+ def yaml_color_to(instance, doc)
113
+ yaml_put(doc, "color", instance.color&.value)
104
114
  end
105
115
 
106
116
  def yaml_highlight_from(instance, value)
107
117
  instance.highlight = Properties::Highlight.new(value: value) if value
108
118
  end
109
119
 
110
- def yaml_highlight_to(_instance, _doc)
111
- highlight&.value
120
+ def yaml_highlight_to(instance, doc)
121
+ yaml_put(doc, "highlight", instance.highlight&.value)
112
122
  end
113
123
 
114
124
  # --- Font transforms ---
@@ -118,8 +128,10 @@ module Uniword
118
128
  instance.fonts.ascii = value if value
119
129
  end
120
130
 
121
- def yaml_font_to(_instance, _doc)
122
- fonts&.ascii
131
+ # "font" and "font_ascii" both name w:rFonts/@w:ascii; write the
132
+ # short spelling once.
133
+ def yaml_font_to(instance, doc)
134
+ yaml_put(doc, "font", instance.fonts&.ascii)
123
135
  end
124
136
 
125
137
  def yaml_font_ascii_from(instance, value)
@@ -127,8 +139,8 @@ module Uniword
127
139
  instance.fonts.ascii = value if value
128
140
  end
129
141
 
130
- def yaml_font_ascii_to(_instance, _doc)
131
- fonts&.ascii
142
+ def yaml_font_ascii_to(instance, doc)
143
+ yaml_font_to(instance, doc)
132
144
  end
133
145
 
134
146
  def yaml_font_east_asia_from(instance, value)
@@ -136,8 +148,8 @@ module Uniword
136
148
  instance.fonts.east_asia = value if value
137
149
  end
138
150
 
139
- def yaml_font_east_asia_to(_instance, _doc)
140
- fonts&.east_asia
151
+ def yaml_font_east_asia_to(instance, doc)
152
+ yaml_put(doc, "font_east_asia", instance.fonts&.east_asia)
141
153
  end
142
154
 
143
155
  def yaml_font_h_ansi_from(instance, value)
@@ -145,8 +157,8 @@ module Uniword
145
157
  instance.fonts.h_ansi = value if value
146
158
  end
147
159
 
148
- def yaml_font_h_ansi_to(_instance, _doc)
149
- fonts&.h_ansi
160
+ def yaml_font_h_ansi_to(instance, doc)
161
+ yaml_put(doc, "font_h_ansi", instance.fonts&.h_ansi)
150
162
  end
151
163
 
152
164
  def yaml_font_cs_from(instance, value)
@@ -154,8 +166,8 @@ module Uniword
154
166
  instance.fonts.cs = value if value
155
167
  end
156
168
 
157
- def yaml_font_cs_to(_instance, _doc)
158
- fonts&.cs
169
+ def yaml_font_cs_to(instance, doc)
170
+ yaml_put(doc, "font_cs", instance.fonts&.cs)
159
171
  end
160
172
 
161
173
  # --- Effect transforms ---
@@ -164,40 +176,45 @@ module Uniword
164
176
  instance.emboss = Properties::Emboss.new(value: value) unless value.nil?
165
177
  end
166
178
 
167
- def yaml_emboss_to(_instance, _doc)
168
- emboss&.value
179
+ def yaml_emboss_to(instance, doc)
180
+ yaml_put(doc, "emboss", instance.emboss&.on?)
169
181
  end
170
182
 
171
183
  def yaml_imprint_from(instance, value)
172
184
  instance.imprint = Properties::Imprint.new(value: value) unless value.nil?
173
185
  end
174
186
 
175
- def yaml_imprint_to(_instance, _doc)
176
- imprint&.value
187
+ def yaml_imprint_to(instance, doc)
188
+ yaml_put(doc, "imprint", instance.imprint&.on?)
177
189
  end
178
190
 
179
191
  def yaml_shadow_from(instance, value)
180
192
  instance.shadow = Properties::Shadow.new(value: value) unless value.nil?
181
193
  end
182
194
 
183
- def yaml_shadow_to(_instance, _doc)
184
- shadow&.value
195
+ def yaml_shadow_to(instance, doc)
196
+ yaml_put(doc, "shadow", instance.shadow&.on?)
185
197
  end
186
198
 
199
+ # The YAML key "outline" carries w:outlineLvl, not the w:outline
200
+ # toggle. Kept as-is so existing style YAML keeps loading.
187
201
  def yaml_outline_from(instance, value)
188
202
  instance.outline_level = Properties::OutlineLevel.new(value: value) unless value.nil?
189
203
  end
190
204
 
191
- def yaml_outline_to(_instance, _doc)
192
- outline_level.value if outline_level.is_a?(Properties::OutlineLevel)
205
+ def yaml_outline_to(instance, doc)
206
+ level = instance.outline_level
207
+ return unless level.is_a?(Properties::OutlineLevel)
208
+
209
+ yaml_put(doc, "outline", level.value)
193
210
  end
194
211
 
195
212
  def yaml_vertical_align_from(instance, value)
196
213
  instance.vertical_align = Properties::VerticalAlign.new(value: value) if value
197
214
  end
198
215
 
199
- def yaml_vertical_align_to(_instance, _doc)
200
- vertical_align&.value
216
+ def yaml_vertical_align_to(instance, doc)
217
+ yaml_put(doc, "vertical_align", instance.vertical_align&.value)
201
218
  end
202
219
  end
203
220
  end
@@ -24,6 +24,12 @@ module Uniword
24
24
 
25
25
  # Pattern 0: ATTRIBUTES FIRST, then XML mappings
26
26
 
27
+ # Paragraph-mark revision markers. Only valid when this rPr is
28
+ # the w:pPr/w:rPr (CT_ParaRPr); Word marks a deleted or inserted
29
+ # paragraph mark with w:del / w:ins there during change tracking.
30
+ attribute :del, DeletedParagraphMark, default: nil
31
+ attribute :ins, InsertedParagraphMark, default: nil
32
+
27
33
  # Simple element wrapper objects (default: nil so unset properties are nil)
28
34
  attribute :style, Properties::RunStyleReference, default: nil
29
35
  attribute :size, Properties::FontSize, default: nil
@@ -126,6 +132,10 @@ module Uniword
126
132
  # outline, shadow, emboss, imprint, noProof, snapToGrid, vanish, webHidden,
127
133
  # color, spacing, w, kern, position, sz, szCs, highlight, u, effect, bdr,
128
134
  # shd, fitText, vertAlign, rtl, cs, em, lang, eastAsianLayout, specVanish, oMath
135
+ # CT_ParaRPr additionally opens with ins, del, moveFrom, moveTo before
136
+ # the shared sequence; the paragraph-mark revision markers come first.
137
+ map_element "ins", to: :ins, render_nil: false
138
+ map_element "del", to: :del, render_nil: false
129
139
  map_element "rStyle", to: :style, render_nil: false
130
140
  map_element "rFonts", to: :fonts, render_nil: false
131
141
  map_element "b", to: :bold, render_nil: false
@@ -5,12 +5,23 @@ require "lutaml/model"
5
5
  module Uniword
6
6
  module Wordprocessingml
7
7
  class StructuredDocumentTag
8
- # Showing placeholder header flag for Structured Document Tag (empty element)
9
- # Reference XML: <w:showingPlcHdr/>
8
+ # Showing placeholder header flag for Structured Document Tag
9
+ # Reference XML: <w:showingPlcHdr/> or <w:showingPlcHdr w:val="0"/>
10
+ #
11
+ # This is an ST_OnOff toggle like every other one. It used to map no
12
+ # w:val at all, so an explicitly-off flag round-tripped as on and the
13
+ # four spellings were indistinguishable.
10
14
  class ShowingPlaceholderHeader < Lutaml::Model::Serializable
15
+ include Uniword::Properties::BooleanElement
16
+
17
+ attribute :val, :string, default: nil
18
+ include Uniword::Properties::BooleanValSetter
19
+
11
20
  xml do
12
21
  element "showingPlcHdr"
13
22
  namespace Ooxml::Namespaces::WordProcessingML
23
+ map_attribute "val", to: :val, render_nil: false,
24
+ render_default: false
14
25
  end
15
26
  end
16
27
  end
@@ -5,13 +5,24 @@ require "lutaml/model"
5
5
  module Uniword
6
6
  module Wordprocessingml
7
7
  class StructuredDocumentTag
8
- # Temporary flag for Structured Document Tag (empty element)
8
+ # Temporary flag for Structured Document Tag
9
9
  # Indicates the SDT should be removed when content is first edited
10
- # Reference XML: <w:temporary/>
10
+ # Reference XML: <w:temporary/> or <w:temporary w:val="0"/>
11
+ #
12
+ # This is an ST_OnOff toggle like every other one. It used to map no
13
+ # w:val at all, so an explicitly-off flag round-tripped as on and the
14
+ # six spellings were indistinguishable.
11
15
  class Temporary < Lutaml::Model::Serializable
16
+ include Uniword::Properties::BooleanElement
17
+
18
+ attribute :val, :string, default: nil
19
+ include Uniword::Properties::BooleanValSetter
20
+
12
21
  xml do
13
22
  element "temporary"
14
23
  namespace Ooxml::Namespaces::WordProcessingML
24
+ map_attribute "val", to: :val, render_nil: false,
25
+ render_default: false
15
26
  end
16
27
  end
17
28
  end
@@ -9,6 +9,8 @@ module Uniword
9
9
  # Generated from OOXML schema: wordprocessingml.yml
10
10
  # Element: <w:style>
11
11
  class Style < Lutaml::Model::Serializable
12
+ include YamlWriter
13
+
12
14
  # Pattern 0: ATTRIBUTES FIRST
13
15
  attribute :type, :string
14
16
  attribute :styleId, :string
@@ -50,53 +52,57 @@ module Uniword
50
52
  end
51
53
 
52
54
  # YAML transform methods (instance methods called by lutaml-model's
53
- # `with:` transform mechanism)
55
+ # `with:` transform mechanism). The `_from` readers build wrapper
56
+ # objects; the `_to` writers assign through YamlWriter#yaml_put rather
57
+ # than returning a value — see that module for why.
54
58
  def yaml_name_from(instance, value)
55
59
  instance.name = StyleName.new(val: value) if value
56
60
  end
57
61
 
58
- def yaml_name_to(instance, _doc)
59
- instance.name&.val
62
+ def yaml_name_to(instance, doc)
63
+ yaml_put(doc, "name", instance.name&.val)
60
64
  end
61
65
 
62
66
  def yaml_quick_format_from(instance, value)
63
67
  instance.qFormat = Properties::QuickFormat.new(value: value) unless value.nil?
64
68
  end
65
69
 
66
- def yaml_quick_format_to(instance, _doc)
67
- instance.qFormat&.value
70
+ # w:qFormat is an ST_OnOff toggle, read through BooleanElement#on?
71
+ # like every other one.
72
+ def yaml_quick_format_to(instance, doc)
73
+ yaml_put(doc, "quick_format", instance.qFormat&.on?)
68
74
  end
69
75
 
70
76
  def yaml_based_on_from(instance, value)
71
77
  instance.basedOn = BasedOn.new(val: value) if value
72
78
  end
73
79
 
74
- def yaml_based_on_to(instance, _doc)
75
- instance.basedOn&.val
80
+ def yaml_based_on_to(instance, doc)
81
+ yaml_put(doc, "based_on", instance.basedOn&.val)
76
82
  end
77
83
 
78
84
  def yaml_next_style_from(instance, value)
79
85
  instance.nextStyle = Next.new(val: value) if value
80
86
  end
81
87
 
82
- def yaml_next_style_to(instance, _doc)
83
- instance.nextStyle&.val
88
+ def yaml_next_style_to(instance, doc)
89
+ yaml_put(doc, "next_style", instance.nextStyle&.val)
84
90
  end
85
91
 
86
92
  def yaml_linked_style_from(instance, value)
87
93
  instance.link = Link.new(val: value) if value
88
94
  end
89
95
 
90
- def yaml_linked_style_to(instance, _doc)
91
- instance.link&.val
96
+ def yaml_linked_style_to(instance, doc)
97
+ yaml_put(doc, "linked_style", instance.link&.val)
92
98
  end
93
99
 
94
100
  def yaml_ui_priority_from(instance, value)
95
101
  instance.uiPriority = UiPriority.new(val: value.to_s) if value
96
102
  end
97
103
 
98
- def yaml_ui_priority_to(instance, _doc)
99
- instance.uiPriority&.val&.to_i
104
+ def yaml_ui_priority_to(instance, doc)
105
+ yaml_put(doc, "ui_priority", instance.uiPriority&.val&.to_i)
100
106
  end
101
107
 
102
108
  xml do
@@ -152,19 +158,18 @@ module Uniword
152
158
  end
153
159
 
154
160
  def quick_format
155
- val = qFormat
156
- return true if val == true
157
-
158
- val = val.value if val.is_a?(Uniword::Properties::BooleanElement)
159
- val == true
161
+ boolean_flag(qFormat) == true
160
162
  end
161
163
 
164
+ # Word splits one paragraph's spacing across several w:spacing elements,
165
+ # and Word readers resolve conflicts last-wins, so take the last entry
166
+ # that actually carries the value.
162
167
  def spacing_before
163
- pPr&.spacing&.before || pPr&.spacing_before
168
+ Array(pPr&.spacing).filter_map(&:before).last || pPr&.spacing_before
164
169
  end
165
170
 
166
171
  def spacing_after
167
- pPr&.spacing&.after || pPr&.spacing_after
172
+ Array(pPr&.spacing).filter_map(&:after).last || pPr&.spacing_after
168
173
  end
169
174
 
170
175
  def alignment
@@ -172,23 +177,11 @@ module Uniword
172
177
  end
173
178
 
174
179
  def keep_next
175
- return false unless pPr
176
-
177
- val = pPr.keep_next_wrapper
178
- return false if val.nil?
179
-
180
- val = val.value if val.is_a?(Uniword::Properties::BooleanElement)
181
- val == true
180
+ boolean_flag(pPr&.keep_next_wrapper) == true
182
181
  end
183
182
 
184
183
  def keep_lines
185
- return false unless pPr
186
-
187
- val = pPr.keep_lines_wrapper
188
- return false if val.nil?
189
-
190
- val = val.value if val.is_a?(Uniword::Properties::BooleanElement)
191
- val == true
184
+ boolean_flag(pPr&.keep_lines_wrapper) == true
192
185
  end
193
186
 
194
187
  def outline_level
@@ -200,13 +193,11 @@ module Uniword
200
193
  end
201
194
 
202
195
  def bold
203
- return nil unless rPr
204
-
205
- val = rPr.bold
206
- return nil if val.nil?
196
+ boolean_flag(rPr&.bold)
197
+ end
207
198
 
208
- val = val.value if val.is_a?(Uniword::Properties::BooleanElement)
209
- val == true
199
+ def italic
200
+ boolean_flag(rPr&.italic)
210
201
  end
211
202
 
212
203
  def font_size
@@ -252,6 +243,18 @@ module Uniword
252
243
  def run_properties
253
244
  rPr
254
245
  end
246
+
247
+ private
248
+
249
+ # ST_OnOff toggles arrive as BooleanElement wrappers when parsed and as
250
+ # plain booleans when built. Returns nil when the toggle is absent, so
251
+ # readers wanting a strict false-when-absent contract compare == true.
252
+ def boolean_flag(element)
253
+ return nil if element.nil?
254
+ return element.on? if element.is_a?(Uniword::Properties::BooleanElement)
255
+
256
+ element == true
257
+ end
255
258
  end
256
259
  end
257
260
  end
@@ -38,18 +38,13 @@ module Uniword
38
38
  map_element "jc", to: :jc, render_nil: false
39
39
  end
40
40
 
41
- def initialize(attrs = {})
42
- # Handle table_header: keyword for docx-js API compatibility
43
- if attrs.key?(:table_header)
44
- val = attrs.delete(:table_header)
45
- attrs[:tbl_header] = coerce_to_val_int(val)
46
- end
47
- super
48
- end
49
-
50
- private
51
-
52
- def coerce_to_val_int(val)
41
+ # Coerce a table_header value (true/false/Integer/ValInt) into the
42
+ # w:tblHeader model value: false and 0 mean "off", which the
43
+ # element expresses by absence.
44
+ #
45
+ # @param val [Object] raw table_header value
46
+ # @return [ValInt, nil]
47
+ def self.coerce_table_header(val)
53
48
  case val
54
49
  when ValInt then val
55
50
  when true, 1 then ValInt.new(value: 1)
@@ -59,6 +54,15 @@ module Uniword
59
54
  else ValInt.new(value: val.to_i)
60
55
  end
61
56
  end
57
+
58
+ def initialize(attrs = {})
59
+ # Handle table_header: keyword for docx-js API compatibility
60
+ if attrs.key?(:table_header)
61
+ val = attrs.delete(:table_header)
62
+ attrs[:tbl_header] = self.class.coerce_table_header(val)
63
+ end
64
+ super
65
+ end
62
66
  end
63
67
  end
64
68
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Wordprocessingml
5
+ # Internal helper for the models that map YAML keys through `with:`
6
+ # transforms.
7
+ #
8
+ # lutaml-model hands a `to:` method the accumulating hash and throws its
9
+ # return value away, so every writer has to assign into that hash.
10
+ # Returning the value instead made to_yaml emit "--- {}" for a fully
11
+ # populated model.
12
+ module YamlWriter
13
+ # Write one YAML key, skipping the ones this model does not set.
14
+ #
15
+ # @param doc [Hash] Accumulating YAML hash
16
+ # @param key [String] YAML key to write
17
+ # @param value [Object, nil] Value, or nil to leave the key out
18
+ # @return [void]
19
+ def yaml_put(doc, key, value)
20
+ doc[key] = value unless value.nil?
21
+ end
22
+ end
23
+ end
24
+ end
@@ -36,6 +36,12 @@ module Uniword
36
36
  autoload :Insertion, "uniword/wordprocessingml/insertion"
37
37
  autoload :Deletion, "uniword/wordprocessingml/deletion"
38
38
 
39
+ # Tracked paragraph marks (CT_TrackChange inside pPr/rPr)
40
+ autoload :DeletedParagraphMark,
41
+ "uniword/wordprocessingml/deleted_paragraph_mark"
42
+ autoload :InsertedParagraphMark,
43
+ "uniword/wordprocessingml/inserted_paragraph_mark"
44
+
39
45
  autoload :NoBreakHyphen, "uniword/wordprocessingml/no_break_hyphen"
40
46
  autoload :LastRenderedPageBreak,
41
47
  "uniword/wordprocessingml/last_rendered_page_break"
@@ -89,10 +95,13 @@ module Uniword
89
95
  # Document settings and defaults
90
96
  autoload :Settings, "uniword/wordprocessingml/settings"
91
97
  autoload :ProofState, "uniword/wordprocessingml/proof_state"
92
- autoload :StylePaneFormatFilter, "uniword/wordprocessingml/style_pane_format_filter"
98
+ autoload :StylePaneFormatFilter,
99
+ "uniword/wordprocessingml/style_pane_format_filter"
93
100
  autoload :DefaultTabStop, "uniword/wordprocessingml/default_tab_stop"
94
- autoload :CharacterSpacingControl, "uniword/wordprocessingml/character_spacing_control"
95
- autoload :DoNotDisplayPageBoundaries, "uniword/wordprocessingml/do_not_display_page_boundaries"
101
+ autoload :CharacterSpacingControl,
102
+ "uniword/wordprocessingml/character_spacing_control"
103
+ autoload :DoNotDisplayPageBoundaries,
104
+ "uniword/wordprocessingml/do_not_display_page_boundaries"
96
105
  autoload :TrackChanges, "uniword/wordprocessingml/track_changes"
97
106
  autoload :Rsids, "uniword/wordprocessingml/rsids"
98
107
  autoload :RsidRoot, "uniword/wordprocessingml/rsid_root"
@@ -115,7 +124,8 @@ module Uniword
115
124
  autoload :DecimalSymbol, "uniword/wordprocessingml/decimal_symbol"
116
125
  autoload :ListSeparator, "uniword/wordprocessingml/list_separator"
117
126
  autoload :W14DocId, "uniword/wordprocessingml/w14_doc_id"
118
- autoload :W15ChartTrackingRefBased, "uniword/wordprocessingml/w15_chart_tracking_ref_based"
127
+ autoload :W15ChartTrackingRefBased,
128
+ "uniword/wordprocessingml/w15_chart_tracking_ref_based"
119
129
  autoload :W15DocId, "uniword/wordprocessingml/w15_doc_id"
120
130
  autoload :AttachedTemplate, "uniword/wordprocessingml/attached_template"
121
131
  autoload :HdrShapeDefaults, "uniword/wordprocessingml/hdr_shape_defaults"
@@ -238,6 +248,9 @@ module Uniword
238
248
  autoload :HideMark, "uniword/wordprocessingml/hide_mark"
239
249
  autoload :TextDirection, "uniword/wordprocessingml/text_direction"
240
250
 
251
+ # Shared YAML serialization helper mixed into the models below
252
+ autoload :YamlWriter, "uniword/wordprocessingml/yaml_writer"
253
+
241
254
  # Properties classes (consolidated from Ooxml::WordProcessingML)
242
255
  autoload :ParagraphProperties,
243
256
  "uniword/wordprocessingml/paragraph_properties"
data/lib/uniword.rb CHANGED
@@ -94,6 +94,8 @@ module Uniword
94
94
  autoload :DependencyError, "uniword/errors"
95
95
  autoload :UnsupportedOperationError, "uniword/errors"
96
96
  autoload :ConversionError, "uniword/errors"
97
+ autoload :MissingVariablesError, "uniword/errors"
98
+ autoload :FidelityError, "uniword/errors"
97
99
 
98
100
  # Styles (moved to wordprocessingml/styles/)
99
101
  autoload :Styles, "uniword/wordprocessingml/styles"
@@ -147,11 +149,16 @@ module Uniword
147
149
  # Plugin system (validators, transformers, CLI commands).
148
150
  autoload :Plugin, "uniword/plugin"
149
151
 
152
+ # Plain-text (Markdown / AsciiDoc) conversion via Coradoc. The
153
+ # coradoc gems are optional at runtime and lazily required.
154
+ autoload :Converter, "uniword/converter"
155
+
150
156
  # CLI
151
157
  module Cli
152
158
  autoload :Completions, "uniword/cli/completions"
153
159
  autoload :HelpSwitch, "uniword/cli/help_switch"
154
160
  autoload :NoColor, "uniword/cli/no_color"
161
+ autoload :PlainTextCommand, "uniword/cli/plain_text_command"
155
162
  end
156
163
  autoload :CLI, "uniword/cli/main"
157
164
  autoload :GenerateCLI, "uniword/cli/generate_cli"
@@ -165,6 +172,8 @@ module Uniword
165
172
  autoload :ResourcesCLI, "uniword/cli/resources_cli"
166
173
  autoload :ThemeCLI, "uniword/cli/theme_cli"
167
174
  autoload :ReviewCLI, "uniword/cli/review_cli"
175
+ autoload :CommentsCLI, "uniword/cli/comments_cli"
176
+ autoload :RevisionsCLI, "uniword/cli/revisions_cli"
168
177
  autoload :TocCLI, "uniword/cli/toc_cli"
169
178
  autoload :ImagesCLI, "uniword/cli/images_cli"
170
179
  autoload :SpellcheckCLI, "uniword/cli/spellcheck_cli"