uniword 1.0.6 → 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 -46
- data/lib/uniword/docx/reconciler.rb +5 -5
- 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/serialization/ooxml_serializer.rb +1 -2
- 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,75 +74,57 @@ document_rels)
|
|
|
78
74
|
# Document parts
|
|
79
75
|
if document
|
|
80
76
|
content["word/document.xml"] =
|
|
81
|
-
document
|
|
82
|
-
fix_boolean_elements: true,
|
|
83
|
-
standalone: true)
|
|
77
|
+
serialize_part(document)
|
|
84
78
|
end
|
|
85
79
|
if styles
|
|
86
80
|
content["word/styles.xml"] =
|
|
87
|
-
styles
|
|
88
|
-
fix_boolean_elements: true,
|
|
89
|
-
standalone: true)
|
|
81
|
+
serialize_part(styles)
|
|
90
82
|
end
|
|
91
83
|
if numbering
|
|
92
84
|
content["word/numbering.xml"] =
|
|
93
|
-
numbering
|
|
94
|
-
fix_boolean_elements: true,
|
|
95
|
-
standalone: true)
|
|
85
|
+
serialize_part(numbering)
|
|
96
86
|
end
|
|
97
87
|
if settings
|
|
98
88
|
content["word/settings.xml"] =
|
|
99
|
-
settings
|
|
100
|
-
standalone: true)
|
|
89
|
+
serialize_part(settings)
|
|
101
90
|
end
|
|
102
91
|
if font_table
|
|
103
92
|
content["word/fontTable.xml"] =
|
|
104
|
-
font_table
|
|
105
|
-
standalone: true)
|
|
93
|
+
serialize_part(font_table)
|
|
106
94
|
end
|
|
107
95
|
if web_settings
|
|
108
96
|
content["word/webSettings.xml"] =
|
|
109
|
-
web_settings
|
|
110
|
-
standalone: true)
|
|
97
|
+
serialize_part(web_settings)
|
|
111
98
|
end
|
|
112
99
|
if document_rels
|
|
113
100
|
content["word/_rels/document.xml.rels"] =
|
|
114
|
-
document_rels
|
|
115
|
-
standalone: true)
|
|
101
|
+
serialize_infrastructure(document_rels)
|
|
116
102
|
end
|
|
117
103
|
|
|
118
104
|
# Theme
|
|
119
105
|
if theme
|
|
120
106
|
content["word/theme/theme1.xml"] =
|
|
121
|
-
theme
|
|
122
|
-
standalone: true)
|
|
107
|
+
serialize_part(theme)
|
|
123
108
|
end
|
|
124
109
|
if theme_rels
|
|
125
110
|
content["word/theme/_rels/theme1.xml.rels"] =
|
|
126
|
-
theme_rels
|
|
127
|
-
standalone: true)
|
|
111
|
+
serialize_infrastructure(theme_rels)
|
|
128
112
|
end
|
|
129
113
|
|
|
130
114
|
# Notes
|
|
131
115
|
if footnotes
|
|
132
116
|
content["word/footnotes.xml"] =
|
|
133
|
-
footnotes
|
|
134
|
-
fix_boolean_elements: true,
|
|
135
|
-
standalone: true)
|
|
117
|
+
serialize_part(footnotes)
|
|
136
118
|
end
|
|
137
119
|
if endnotes
|
|
138
120
|
content["word/endnotes.xml"] =
|
|
139
|
-
endnotes
|
|
140
|
-
fix_boolean_elements: true,
|
|
141
|
-
standalone: true)
|
|
121
|
+
serialize_part(endnotes)
|
|
142
122
|
end
|
|
143
123
|
|
|
144
124
|
# Bibliography sources
|
|
145
125
|
if document&.bibliography_sources
|
|
146
126
|
content["word/sources.xml"] =
|
|
147
|
-
document.bibliography_sources
|
|
148
|
-
declaration: true,
|
|
149
|
-
standalone: true)
|
|
127
|
+
serialize_infrastructure(document.bibliography_sources)
|
|
150
128
|
end
|
|
151
129
|
|
|
152
130
|
# Headers and footers
|
|
@@ -155,6 +133,16 @@ document_rels)
|
|
|
155
133
|
serialize_header_footer_parts(content)
|
|
156
134
|
end
|
|
157
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
|
+
|
|
158
146
|
private
|
|
159
147
|
|
|
160
148
|
def inject_image_parts(content, content_types, document_rels)
|
|
@@ -456,9 +444,7 @@ document_rels)
|
|
|
456
444
|
document.headers.each_value do |header_obj|
|
|
457
445
|
idx += 1
|
|
458
446
|
content["word/header#{idx}.xml"] =
|
|
459
|
-
header_obj
|
|
460
|
-
fix_boolean_elements: true,
|
|
461
|
-
standalone: true)
|
|
447
|
+
serialize_part(header_obj)
|
|
462
448
|
end
|
|
463
449
|
end
|
|
464
450
|
|
|
@@ -469,9 +455,7 @@ document_rels)
|
|
|
469
455
|
document.footers.each_value do |footer_obj|
|
|
470
456
|
idx += 1
|
|
471
457
|
content["word/footer#{idx}.xml"] =
|
|
472
|
-
footer_obj
|
|
473
|
-
fix_boolean_elements: true,
|
|
474
|
-
standalone: true)
|
|
458
|
+
serialize_part(footer_obj)
|
|
475
459
|
end
|
|
476
460
|
end
|
|
477
461
|
|
|
@@ -480,9 +464,7 @@ document_rels)
|
|
|
480
464
|
|
|
481
465
|
document.header_footer_parts.each do |part|
|
|
482
466
|
content["word/#{part[:target]}"] =
|
|
483
|
-
part[:content]
|
|
484
|
-
fix_boolean_elements: true,
|
|
485
|
-
standalone: true)
|
|
467
|
+
serialize_part(part[:content])
|
|
486
468
|
end
|
|
487
469
|
end
|
|
488
470
|
end
|
|
@@ -85,14 +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
94
|
end
|
|
92
95
|
end
|
|
93
|
-
|
|
94
|
-
# -- Section Properties --
|
|
95
|
-
|
|
96
96
|
def reconcile_section_properties
|
|
97
97
|
return unless package.document&.body
|
|
98
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
|
|
@@ -15,8 +15,7 @@ module Uniword
|
|
|
15
15
|
# @param document [DocumentRoot] The document to serialize
|
|
16
16
|
# @return [String] The serialized XML
|
|
17
17
|
def serialize(document)
|
|
18
|
-
document.to_xml(encoding: "UTF-8", prefix: true
|
|
19
|
-
fix_boolean_elements: true)
|
|
18
|
+
document.to_xml(encoding: "UTF-8", prefix: true)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
# Serialize a document to XML and return as a Nokogiri document
|
|
@@ -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
|