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 +4 -4
- data/lib/uniword/builder/comment_builder.rb +3 -5
- data/lib/uniword/builder.rb +12 -2
- data/lib/uniword/docx/package_serialization.rb +28 -38
- data/lib/uniword/docx/reconciler.rb +5 -6
- data/lib/uniword/drawingml/color_scheme.rb +2 -2
- data/lib/uniword/math/o_math.rb +3 -0
- data/lib/uniword/properties/boolean_formatting.rb +2 -6
- data/lib/uniword/theme/theme_xml_parser.rb +1 -1
- data/lib/uniword/transformation/mhtml_element_renderer.rb +2 -2
- data/lib/uniword/version.rb +1 -1
- data/lib/uniword/wordprocessingml/hyperlink.rb +3 -0
- data/lib/uniword/wordprocessingml/run.rb +5 -17
- data/lib/uniword/wordprocessingml/style.rb +0 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a62022223f8643943947659aae4d0e27b5d8566cab7486e0c688566cc0428208
|
|
4
|
+
data.tar.gz: 61727290e9fac98b2e4cf3b43bb1fd70f09c4b9648206eb861d1bc3766b964d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
-
|
|
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)
|
data/lib/uniword/builder.rb
CHANGED
|
@@ -148,15 +148,25 @@ module Uniword
|
|
|
148
148
|
run
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
# Factory: creates a Run
|
|
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 =
|
|
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
|
|
37
|
-
standalone: true)
|
|
36
|
+
serialize_infrastructure(content_types)
|
|
38
37
|
content["_rels/.rels"] =
|
|
39
|
-
package_rels
|
|
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
|
|
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
|
|
82
|
-
standalone: true)
|
|
77
|
+
serialize_part(document)
|
|
83
78
|
end
|
|
84
79
|
if styles
|
|
85
80
|
content["word/styles.xml"] =
|
|
86
|
-
styles
|
|
87
|
-
standalone: true)
|
|
81
|
+
serialize_part(styles)
|
|
88
82
|
end
|
|
89
83
|
if numbering
|
|
90
84
|
content["word/numbering.xml"] =
|
|
91
|
-
numbering
|
|
92
|
-
standalone: true)
|
|
85
|
+
serialize_part(numbering)
|
|
93
86
|
end
|
|
94
87
|
if settings
|
|
95
88
|
content["word/settings.xml"] =
|
|
96
|
-
settings
|
|
97
|
-
standalone: true)
|
|
89
|
+
serialize_part(settings)
|
|
98
90
|
end
|
|
99
91
|
if font_table
|
|
100
92
|
content["word/fontTable.xml"] =
|
|
101
|
-
font_table
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
131
|
-
standalone: true)
|
|
117
|
+
serialize_part(footnotes)
|
|
132
118
|
end
|
|
133
119
|
if endnotes
|
|
134
120
|
content["word/endnotes.xml"] =
|
|
135
|
-
endnotes
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
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.
|
|
89
|
-
part.
|
|
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 =
|
|
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
|
-
|
|
348
|
+
public_send(:"#{attr_name}=", color_obj)
|
|
349
349
|
@colors_hash[color_name] = value
|
|
350
350
|
end
|
|
351
351
|
|
data/lib/uniword/math/o_math.rb
CHANGED
|
@@ -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
|
-
|
|
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)
|
|
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.
|
|
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
|
|
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.
|
|
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
|
data/lib/uniword/version.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
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.
|
|
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-
|
|
11
|
+
date: 2026-05-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: logger
|