uniword 1.0.7 → 1.0.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea1c6924b201809b0adcd798ea5453ceb06a6628e9ea54ec30221d4d4b325ca4
4
- data.tar.gz: 45d19527778e0c36b9acb7a2a7ce98e83ea9f841df619c2bf1eb3ae4f77fbc46
3
+ metadata.gz: a62022223f8643943947659aae4d0e27b5d8566cab7486e0c688566cc0428208
4
+ data.tar.gz: 61727290e9fac98b2e4cf3b43bb1fd70f09c4b9648206eb861d1bc3766b964d9
5
5
  SHA512:
6
- metadata.gz: 9ff4355adebd0f8eb10843ca4ae83b43df148340b3ab562d1bd43b1317d6384fd0dd3fe713e741df9d57dbad2eca70684e946565dc9823bb923c75cc2f302226
7
- data.tar.gz: 92b190e3421fce1634462ce0b6bdff183138c6eaefa41ef96cce9b441eba72661988a36ae652fa5df8afdd1ef3eeb3d62bdd9e7eb1cd09b10a7ae7863023b331
6
+ metadata.gz: f99053000864fae5910ee3c34f07504daf4afb061093aced4a5589f0a80c335cca44fa48a72bad5f1725d87a82346dc5abc577c14e6238875a9a175bfd33c788
7
+ data.tar.gz: cf9546e5ad9b15a3d4973785b922589d29bbd8bea8a34c75b1012f10a2a9190988abaccae1c5e0f41217589fc9f27151342210373ca9a00c355f86ad2218b15c
@@ -11,8 +11,8 @@ module Uniword
11
11
  class CommentBuilder
12
12
  attr_reader :model
13
13
 
14
- def initialize(author:, comment_id: nil, date: nil, initials: nil)
15
- @model = Comment.new(
14
+ def initialize(model: nil, author: nil, comment_id: nil, date: nil, initials: nil)
15
+ @model = model || Comment.new(
16
16
  author: author,
17
17
  comment_id: comment_id,
18
18
  date: date,
@@ -22,9 +22,7 @@ module Uniword
22
22
 
23
23
  # Wrap an existing Comment model
24
24
  def self.from_model(model)
25
- builder = allocate
26
- builder.instance_variable_set(:@model, model)
27
- builder
25
+ new(model: model)
28
26
  end
29
27
 
30
28
  # Append text to the comment (creates a paragraph)
@@ -148,15 +148,25 @@ module Uniword
148
148
  run
149
149
  end
150
150
 
151
- # Factory: creates a Run with a tab character
151
+ # Factory: creates a Run containing a tab character.
152
+ # Use this with ParagraphBuilder#<< to append a tab to a paragraph.
152
153
  #
153
154
  # @return [Wordprocessingml::Run]
155
+ # @see .tab_element for the bare Tab element
154
156
  def self.tab
155
157
  run = Wordprocessingml::Run.new
156
- run.tab = Wordprocessingml::Tab.new
158
+ run.tab = tab_element
157
159
  run
158
160
  end
159
161
 
162
+ # Factory: creates a bare Tab element for direct assignment to Run#tab.
163
+ # Most callers should use .tab instead, which wraps the Tab in a Run.
164
+ #
165
+ # @return [Wordprocessingml::Tab]
166
+ def self.tab_element
167
+ Wordprocessingml::Tab.new
168
+ end
169
+
160
170
  # Factory: creates a Run containing a line break
161
171
  #
162
172
  # @return [Wordprocessingml::Run]
@@ -33,18 +33,14 @@ document_rels)
33
33
  document_rels)
34
34
  # Package infrastructure
35
35
  content["[Content_Types].xml"] =
36
- content_types.to_xml(encoding: "UTF-8", declaration: true,
37
- standalone: true)
36
+ serialize_infrastructure(content_types)
38
37
  content["_rels/.rels"] =
39
- package_rels.to_xml(encoding: "UTF-8", declaration: true,
40
- standalone: true)
38
+ serialize_infrastructure(package_rels)
41
39
 
42
40
  # Document properties
43
41
  if core_properties
44
42
  content["docProps/core.xml"] =
45
- core_properties.to_xml(encoding: "UTF-8",
46
- prefix: true,
47
- standalone: true)
43
+ serialize_part(core_properties)
48
44
  end
49
45
  if app_properties
50
46
  content["docProps/app.xml"] =
@@ -78,70 +74,57 @@ document_rels)
78
74
  # Document parts
79
75
  if document
80
76
  content["word/document.xml"] =
81
- document.to_xml(encoding: "UTF-8", prefix: true,
82
- standalone: true)
77
+ serialize_part(document)
83
78
  end
84
79
  if styles
85
80
  content["word/styles.xml"] =
86
- styles.to_xml(encoding: "UTF-8", prefix: true,
87
- standalone: true)
81
+ serialize_part(styles)
88
82
  end
89
83
  if numbering
90
84
  content["word/numbering.xml"] =
91
- numbering.to_xml(encoding: "UTF-8", prefix: true,
92
- standalone: true)
85
+ serialize_part(numbering)
93
86
  end
94
87
  if settings
95
88
  content["word/settings.xml"] =
96
- settings.to_xml(encoding: "UTF-8", prefix: true,
97
- standalone: true)
89
+ serialize_part(settings)
98
90
  end
99
91
  if font_table
100
92
  content["word/fontTable.xml"] =
101
- font_table.to_xml(encoding: "UTF-8", prefix: true,
102
- standalone: true)
93
+ serialize_part(font_table)
103
94
  end
104
95
  if web_settings
105
96
  content["word/webSettings.xml"] =
106
- web_settings.to_xml(encoding: "UTF-8", prefix: true,
107
- standalone: true)
97
+ serialize_part(web_settings)
108
98
  end
109
99
  if document_rels
110
100
  content["word/_rels/document.xml.rels"] =
111
- document_rels.to_xml(encoding: "UTF-8", declaration: true,
112
- standalone: true)
101
+ serialize_infrastructure(document_rels)
113
102
  end
114
103
 
115
104
  # Theme
116
105
  if theme
117
106
  content["word/theme/theme1.xml"] =
118
- theme.to_xml(encoding: "UTF-8", prefix: true,
119
- standalone: true)
107
+ serialize_part(theme)
120
108
  end
121
109
  if theme_rels
122
110
  content["word/theme/_rels/theme1.xml.rels"] =
123
- theme_rels.to_xml(encoding: "UTF-8", declaration: true,
124
- standalone: true)
111
+ serialize_infrastructure(theme_rels)
125
112
  end
126
113
 
127
114
  # Notes
128
115
  if footnotes
129
116
  content["word/footnotes.xml"] =
130
- footnotes.to_xml(encoding: "UTF-8", prefix: true,
131
- standalone: true)
117
+ serialize_part(footnotes)
132
118
  end
133
119
  if endnotes
134
120
  content["word/endnotes.xml"] =
135
- endnotes.to_xml(encoding: "UTF-8", prefix: true,
136
- standalone: true)
121
+ serialize_part(endnotes)
137
122
  end
138
123
 
139
124
  # Bibliography sources
140
125
  if document&.bibliography_sources
141
126
  content["word/sources.xml"] =
142
- document.bibliography_sources.to_xml(encoding: "UTF-8",
143
- declaration: true,
144
- standalone: true)
127
+ serialize_infrastructure(document.bibliography_sources)
145
128
  end
146
129
 
147
130
  # Headers and footers
@@ -150,6 +133,16 @@ document_rels)
150
133
  serialize_header_footer_parts(content)
151
134
  end
152
135
 
136
+ # Serialize an OOXML document part with standard encoding
137
+ def serialize_part(model)
138
+ model.to_xml(encoding: "UTF-8", prefix: true, standalone: true)
139
+ end
140
+
141
+ # Serialize package infrastructure (rels, content types) with declaration
142
+ def serialize_infrastructure(model)
143
+ model.to_xml(encoding: "UTF-8", declaration: true, standalone: true)
144
+ end
145
+
153
146
  private
154
147
 
155
148
  def inject_image_parts(content, content_types, document_rels)
@@ -451,8 +444,7 @@ document_rels)
451
444
  document.headers.each_value do |header_obj|
452
445
  idx += 1
453
446
  content["word/header#{idx}.xml"] =
454
- header_obj.to_xml(encoding: "UTF-8", prefix: true,
455
- standalone: true)
447
+ serialize_part(header_obj)
456
448
  end
457
449
  end
458
450
 
@@ -463,8 +455,7 @@ document_rels)
463
455
  document.footers.each_value do |footer_obj|
464
456
  idx += 1
465
457
  content["word/footer#{idx}.xml"] =
466
- footer_obj.to_xml(encoding: "UTF-8", prefix: true,
467
- standalone: true)
458
+ serialize_part(footer_obj)
468
459
  end
469
460
  end
470
461
 
@@ -473,8 +464,7 @@ document_rels)
473
464
 
474
465
  document.header_footer_parts.each do |part|
475
466
  content["word/#{part[:target]}"] =
476
- part[:content].to_xml(encoding: "UTF-8", prefix: true,
477
- standalone: true)
467
+ serialize_part(part[:content])
478
468
  end
479
469
  end
480
470
  end
@@ -85,15 +85,14 @@ module Uniword
85
85
  ].compact
86
86
 
87
87
  parts.each do |part|
88
- part.instance_variable_set(:@pending_namespace_data, nil)
89
- part.instance_variable_set(:@import_declaration_plan, nil)
88
+ part.pending_namespace_data = nil
89
+ part.import_declaration_plan = nil
90
+ part.pending_plan_root_element = nil
91
+ # TODO: @xml_input_namespaces has no public accessor in lutaml-model.
92
+ # Replace with public API once lutaml-model exposes one.
90
93
  part.instance_variable_set(:@xml_input_namespaces, nil)
91
- part.instance_variable_set(:@pending_plan_root_element, nil)
92
94
  end
93
95
  end
94
-
95
- # -- Section Properties --
96
-
97
96
  def reconcile_section_properties
98
97
  return unless package.document&.body
99
98
 
@@ -313,7 +313,7 @@ module Uniword
313
313
  color_name = color_name.to_sym
314
314
  # Map folHlink to fol_hlink for attribute access
315
315
  attr_name = color_name == :folHlink ? :fol_hlink : color_name
316
- color_obj = instance_variable_get("@#{attr_name}")
316
+ color_obj = public_send(attr_name)
317
317
  color_obj&.value
318
318
  end
319
319
 
@@ -345,7 +345,7 @@ module Uniword
345
345
 
346
346
  color_obj = color_class.new
347
347
  color_obj.rgb = value
348
- instance_variable_set("@#{attr_name}", color_obj)
348
+ public_send(:"#{attr_name}=", color_obj)
349
349
  @colors_hash[color_name] = value
350
350
  end
351
351
 
@@ -32,6 +32,9 @@ module Uniword
32
32
  attribute :equation_arrays, EquationArray, collection: true,
33
33
  initialize_empty: true
34
34
 
35
+ # Transient: position among sibling runs (used by MHTML renderer)
36
+ attribute :run_position, :integer
37
+
35
38
  xml do
36
39
  element "oMath"
37
40
  namespace Uniword::Ooxml::Namespaces::MathML
@@ -9,11 +9,7 @@ module Uniword
9
9
  # the generated setter.
10
10
  module BooleanElement
11
11
  def value
12
- @val != "false"
13
- end
14
-
15
- def value=(v)
16
- @val = v ? nil : "false"
12
+ val != "false"
17
13
  end
18
14
  end
19
15
 
@@ -35,7 +31,7 @@ module Uniword
35
31
  else
36
32
  v
37
33
  end
38
- value_set_for(:val) if @val
34
+ value_set_for(:val)
39
35
  end
40
36
  end
41
37
  end
@@ -79,7 +79,7 @@ module Uniword
79
79
 
80
80
  # Set the color using the appropriate attribute
81
81
  attr_name = color_name == "folHlink" ? :fol_hlink : color_name.to_sym
82
- scheme.instance_variable_set("@#{attr_name}", color_obj)
82
+ scheme.public_send(:"#{attr_name}=", color_obj)
83
83
  end
84
84
 
85
85
  # Sync hash interface
@@ -543,11 +543,11 @@ module Uniword
543
543
  parts.join
544
544
  end
545
545
 
546
- # Build position-indexed map from elements that have @_run_position markers
546
+ # Build position-indexed map from elements that have run_position markers
547
547
  def build_position_map(elements, fallback_pos)
548
548
  by_pos = {}
549
549
  elements.each do |el|
550
- pos = el.instance_variable_get(:@_run_position)
550
+ pos = el.run_position if el.is_a?(Uniword::Wordprocessingml::Hyperlink) || el.is_a?(Uniword::Math::OMath)
551
551
  pos ||= fallback_pos
552
552
  by_pos[pos] ||= []
553
553
  by_pos[pos] << el
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uniword
4
- VERSION = "1.0.7"
4
+ VERSION = "1.0.8"
5
5
  end
@@ -15,6 +15,9 @@ module Uniword
15
15
  attribute :history, Uniword::Properties::HistoryValue
16
16
  attribute :runs, Run, collection: true, initialize_empty: true
17
17
 
18
+ # Transient: position among sibling runs (used by MHTML renderer)
19
+ attribute :run_position, :integer
20
+
18
21
  xml do
19
22
  element "hyperlink"
20
23
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
@@ -178,34 +178,22 @@ module Uniword
178
178
  def merge_properties(base, override)
179
179
  merged = RunProperties.new
180
180
 
181
- # Get all attribute names from RunProperties class
182
181
  RunProperties.attributes.each_key do |attr_name|
183
182
  override_val = override.public_send(attr_name)
184
183
  base_val = base.public_send(attr_name)
185
184
 
186
- # Use override if it's non-nil AND not using default value
187
- # For boolean properties like Bold, check if it was explicitly set
188
185
  use_override = if override_val.is_a?(Lutaml::Model::Serializable)
189
- # Check if the property has any non-default/non-nil values
190
186
  override_val.class.attributes.any? do |k, _|
191
- iv = override_val.instance_variable_get(:"@#{k}")
192
- iv && !override_val.using_default?(k)
187
+ !override_val.using_default?(k)
193
188
  end
194
189
  else
195
190
  !override_val.nil?
196
191
  end
197
192
 
198
- begin
199
- if use_override
200
- merged.public_send(:"#{attr_name}=", override_val)
201
- elsif base_val
202
- merged.public_send(:"#{attr_name}=", base_val)
203
- end
204
- rescue StandardError => e
205
- Uniword.logger&.debug do
206
- "Skipping attribute #{attr_name}: #{e.message}"
207
- end
208
- end
193
+ value = use_override ? override_val : base_val
194
+ next unless value
195
+
196
+ merged.public_send(:"#{attr_name}=", value)
209
197
  end
210
198
 
211
199
  merged
@@ -251,10 +251,6 @@ module Uniword
251
251
  def run_properties
252
252
  rPr
253
253
  end
254
-
255
- def to_xml(options = {})
256
- super
257
- end
258
254
  end
259
255
  end
260
256
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uniword
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-08 00:00:00.000000000 Z
11
+ date: 2026-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logger