uniword 1.0.7 → 1.0.9
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 +4 -5
- data/lib/uniword/builder.rb +12 -2
- data/lib/uniword/document_input.rb +7 -0
- data/lib/uniword/document_writer.rb +1 -5
- data/lib/uniword/docx/package_serialization.rb +28 -38
- data/lib/uniword/docx/reconciler.rb +5 -5
- data/lib/uniword/drawingml/color_scheme.rb +2 -2
- data/lib/uniword/format_converter.rb +4 -19
- data/lib/uniword/has_run_position.rb +9 -0
- data/lib/uniword/math/o_math.rb +6 -0
- data/lib/uniword/mhtml/document.rb +18 -0
- data/lib/uniword/properties/bold.rb +3 -33
- data/lib/uniword/properties/boolean_element_factory.rb +73 -0
- data/lib/uniword/properties/boolean_formatting.rb +15 -219
- data/lib/uniword/properties/italic.rb +3 -33
- data/lib/uniword/properties.rb +8 -3
- 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/document_root.rb +11 -0
- data/lib/uniword/wordprocessingml/hyperlink.rb +6 -0
- data/lib/uniword/wordprocessingml/run.rb +5 -17
- data/lib/uniword/wordprocessingml/style.rb +0 -4
- metadata +5 -3
- data/lib/uniword/ooxml/types/on_off_type.rb +0 -64
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b3b37b9a1642f94a634c42b754630ca35cd7167ddc1415223dd65848a379c48
|
|
4
|
+
data.tar.gz: 011ddc8e381521e2d161cd0cf9fd0dd9eee791e9f28026fd63e89cf9386bfa03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c6c8785a7fe56480f0d7bdda19ec56e50704653016d4146c561e401c8e98b856cf99a1205411521b78cd19b701e794dfbc75c4d4c844b87e2b8fe3d44fb514e
|
|
7
|
+
data.tar.gz: 9275ea155ee7af2ee9cd0e24d6534a0124f0f8501583206fb98eb3c63e7736c280552336a47bad97a31e50b235805c5fba904d1a80c0cee9bbe6f216f7e49f02
|
|
@@ -11,8 +11,9 @@ module Uniword
|
|
|
11
11
|
class CommentBuilder
|
|
12
12
|
attr_reader :model
|
|
13
13
|
|
|
14
|
-
def initialize(author
|
|
15
|
-
|
|
14
|
+
def initialize(model: nil, author: nil, comment_id: nil, date: nil,
|
|
15
|
+
initials: nil)
|
|
16
|
+
@model = model || Comment.new(
|
|
16
17
|
author: author,
|
|
17
18
|
comment_id: comment_id,
|
|
18
19
|
date: date,
|
|
@@ -22,9 +23,7 @@ module Uniword
|
|
|
22
23
|
|
|
23
24
|
# Wrap an existing Comment model
|
|
24
25
|
def self.from_model(model)
|
|
25
|
-
|
|
26
|
-
builder.instance_variable_set(:@model, model)
|
|
27
|
-
builder
|
|
26
|
+
new(model: model)
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
# 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]
|
|
@@ -136,11 +136,7 @@ module Uniword
|
|
|
136
136
|
# @raise [ArgumentError] if document is invalid
|
|
137
137
|
def validate_document(doc)
|
|
138
138
|
raise ArgumentError, "Document cannot be nil" if doc.nil?
|
|
139
|
-
|
|
140
|
-
# Accept OOXML Document
|
|
141
|
-
return if doc.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
142
|
-
# Accept MHTML Document
|
|
143
|
-
return if doc.is_a?(Uniword::Mhtml::Document)
|
|
139
|
+
return if doc.is_a?(Uniword::DocumentInput)
|
|
144
140
|
|
|
145
141
|
raise ArgumentError, "Must be a Document instance"
|
|
146
142
|
end
|
|
@@ -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,15 @@ 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
96
|
|
|
95
|
-
# -- Section Properties --
|
|
96
|
-
|
|
97
97
|
def reconcile_section_properties
|
|
98
98
|
return unless package.document&.body
|
|
99
99
|
|
|
@@ -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
|
|
|
@@ -277,30 +277,15 @@ module Uniword
|
|
|
277
277
|
# @param document [Object] Document (OOXML or MHTML)
|
|
278
278
|
# @return [Hash] Statistics hash
|
|
279
279
|
def document_stats(document)
|
|
280
|
-
|
|
281
|
-
if document.is_a?(Uniword::Wordprocessingml::DocumentRoot) || document.is_a?(Uniword::Wordprocessingml::Body)
|
|
280
|
+
if document.is_a?(Uniword::Wordprocessingml::Body)
|
|
282
281
|
return {
|
|
283
282
|
paragraphs: document.paragraphs.count,
|
|
284
|
-
tables:
|
|
285
|
-
images:
|
|
283
|
+
tables: 0,
|
|
284
|
+
images: 0
|
|
286
285
|
}
|
|
287
286
|
end
|
|
288
287
|
|
|
289
|
-
|
|
290
|
-
html = if document.is_a?(Mhtml::Document) && document.raw_html
|
|
291
|
-
document.raw_html
|
|
292
|
-
elsif document.is_a?(Mhtml::Document) && document.html_content
|
|
293
|
-
document.html_content
|
|
294
|
-
end
|
|
295
|
-
if html
|
|
296
|
-
{
|
|
297
|
-
paragraphs: html.scan(/<p[\s>]/i).count,
|
|
298
|
-
tables: html.scan(/<table/i).count,
|
|
299
|
-
images: html.scan(/<img/i).count
|
|
300
|
-
}
|
|
301
|
-
else
|
|
302
|
-
{ paragraphs: 0, tables: 0, images: 0 }
|
|
303
|
-
end
|
|
288
|
+
document.document_stats
|
|
304
289
|
end
|
|
305
290
|
|
|
306
291
|
# Validate conversion parameters
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Uniword
|
|
4
|
+
# Tagging module for elements that track their position among sibling runs.
|
|
5
|
+
# Used by the MHTML renderer to interleave hyperlinks and math objects
|
|
6
|
+
# at the correct position within a paragraph.
|
|
7
|
+
module HasRunPosition
|
|
8
|
+
end
|
|
9
|
+
end
|
data/lib/uniword/math/o_math.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
|
+
require_relative "../has_run_position"
|
|
4
5
|
|
|
5
6
|
module Uniword
|
|
6
7
|
module Math
|
|
@@ -9,6 +10,8 @@ module Uniword
|
|
|
9
10
|
# Generated from OOXML schema: math.yml
|
|
10
11
|
# Element: <m:oMath>
|
|
11
12
|
class OMath < Lutaml::Model::Serializable
|
|
13
|
+
include Uniword::HasRunPosition
|
|
14
|
+
|
|
12
15
|
# Pattern 0: Attributes BEFORE xml mappings
|
|
13
16
|
attribute :runs, MathRun, collection: true, initialize_empty: true
|
|
14
17
|
attribute :functions, Function, collection: true, initialize_empty: true
|
|
@@ -32,6 +35,9 @@ module Uniword
|
|
|
32
35
|
attribute :equation_arrays, EquationArray, collection: true,
|
|
33
36
|
initialize_empty: true
|
|
34
37
|
|
|
38
|
+
# Transient: position among sibling runs (used by MHTML renderer)
|
|
39
|
+
attribute :run_position, :integer
|
|
40
|
+
|
|
35
41
|
xml do
|
|
36
42
|
element "oMath"
|
|
37
43
|
namespace Uniword::Ooxml::Namespaces::MathML
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
4
|
|
|
5
|
+
require_relative "../document_input"
|
|
6
|
+
|
|
5
7
|
module Uniword
|
|
6
8
|
module Mhtml
|
|
7
9
|
# MHTML Document — top-level model for .mht/.mhtml/.doc files.
|
|
@@ -17,6 +19,8 @@ module Uniword
|
|
|
17
19
|
# ├── word_document_settings (Metadata::WordDocumentSettings)
|
|
18
20
|
# └── filelist_xml (String)
|
|
19
21
|
class Document < Lutaml::Model::Serializable
|
|
22
|
+
include Uniword::DocumentInput
|
|
23
|
+
|
|
20
24
|
# MIME headers
|
|
21
25
|
attribute :mime_version, :string, default: -> { "1.0" }
|
|
22
26
|
attribute :boundary, :string
|
|
@@ -161,6 +165,20 @@ module Uniword
|
|
|
161
165
|
"#<#{self.class} parts=#{parts.length} images=#{image_parts.length} " \
|
|
162
166
|
"xml=#{xml_parts.length} theme=#{theme_part ? 'yes' : 'no'}>"
|
|
163
167
|
end
|
|
168
|
+
|
|
169
|
+
# @return [Hash] Document statistics (paragraphs, tables, images)
|
|
170
|
+
def document_stats
|
|
171
|
+
html = raw_html || html_content
|
|
172
|
+
if html
|
|
173
|
+
{
|
|
174
|
+
paragraphs: html.scan(/<p[\s>]/i).count,
|
|
175
|
+
tables: html.scan(/<table/i).count,
|
|
176
|
+
images: html.scan(/<img/i).count,
|
|
177
|
+
}
|
|
178
|
+
else
|
|
179
|
+
{ paragraphs: 0, tables: 0, images: 0 }
|
|
180
|
+
end
|
|
181
|
+
end
|
|
164
182
|
end
|
|
165
183
|
end
|
|
166
184
|
end
|
|
@@ -1,40 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "boolean_element_factory"
|
|
4
4
|
|
|
5
5
|
module Uniword
|
|
6
6
|
module Properties
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# OOXML: <w:b/> means bold=true (absence of val= means true)
|
|
10
|
-
# <w:b w:val="false"/> means bold=false
|
|
11
|
-
#
|
|
12
|
-
# Model: nil = true (no val attribute), 'false' = explicit false
|
|
13
|
-
class Bold < Lutaml::Model::Serializable
|
|
14
|
-
include Uniword::Properties::BooleanElement
|
|
15
|
-
|
|
16
|
-
attribute :val, :string, default: nil
|
|
17
|
-
include Uniword::Properties::BooleanValSetter
|
|
18
|
-
|
|
19
|
-
xml do
|
|
20
|
-
element "b"
|
|
21
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
22
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Complex script bold
|
|
27
|
-
class BoldCs < Lutaml::Model::Serializable
|
|
28
|
-
include Uniword::Properties::BooleanElement
|
|
29
|
-
|
|
30
|
-
attribute :val, :string, default: nil
|
|
31
|
-
include Uniword::Properties::BooleanValSetter
|
|
32
|
-
|
|
33
|
-
xml do
|
|
34
|
-
element "bCs"
|
|
35
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
36
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
37
|
-
end
|
|
38
|
-
end
|
|
7
|
+
BooleanElementFactory.define("b", "Bold")
|
|
8
|
+
BooleanElementFactory.define("bCs", "BoldCs")
|
|
39
9
|
end
|
|
40
10
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Uniword
|
|
6
|
+
module Properties
|
|
7
|
+
# OOXML boolean element mixin for value/val getter logic only.
|
|
8
|
+
# The val= setter must be defined AFTER attribute :val to override
|
|
9
|
+
# the generated setter.
|
|
10
|
+
module BooleanElement
|
|
11
|
+
def value
|
|
12
|
+
val != "false"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Helper to define val= override after attribute declaration.
|
|
17
|
+
# Uses alias_method to save the generated setter, then overrides it.
|
|
18
|
+
module BooleanValSetter
|
|
19
|
+
def self.included(base)
|
|
20
|
+
base.class_eval do
|
|
21
|
+
alias_method :__original_val_setter=, :val= if method_defined?(:val=)
|
|
22
|
+
define_method(:val=) do |v|
|
|
23
|
+
@val = if v.nil?
|
|
24
|
+
nil
|
|
25
|
+
elsif v == false || v.to_s == "false"
|
|
26
|
+
"false"
|
|
27
|
+
elsif v == true || v.to_s == "true"
|
|
28
|
+
nil
|
|
29
|
+
else
|
|
30
|
+
v
|
|
31
|
+
end
|
|
32
|
+
value_set_for(:val)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Factory for OOXML boolean element classes.
|
|
39
|
+
#
|
|
40
|
+
# Generates a lutaml-model class that maps to an XML element like
|
|
41
|
+
# <w:b/> (true) or <w:b w:val="false"/> (false).
|
|
42
|
+
#
|
|
43
|
+
# @param element_name [String] XML element name (e.g. "b", "i", "strike")
|
|
44
|
+
# @param class_name [String] Ruby class name (e.g. "Bold", "Italic")
|
|
45
|
+
# @return [Class] The generated class, also assigned as a constant
|
|
46
|
+
#
|
|
47
|
+
# @example
|
|
48
|
+
# BooleanElementFactory.define("b", "Bold")
|
|
49
|
+
# BooleanElementFactory.define("bCs", "BoldCs")
|
|
50
|
+
module BooleanElementFactory
|
|
51
|
+
WML_NS = Uniword::Ooxml::Namespaces::WordProcessingML
|
|
52
|
+
|
|
53
|
+
def self.define(element_name, class_name)
|
|
54
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
55
|
+
include BooleanElement
|
|
56
|
+
|
|
57
|
+
attribute :val, :string, default: nil
|
|
58
|
+
include BooleanValSetter
|
|
59
|
+
|
|
60
|
+
xml do
|
|
61
|
+
element element_name
|
|
62
|
+
namespace WML_NS
|
|
63
|
+
map_attribute "val", to: :val, render_nil: false,
|
|
64
|
+
render_default: false
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
Properties.const_set(class_name, klass)
|
|
69
|
+
klass
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -1,228 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "boolean_element_factory"
|
|
4
4
|
|
|
5
5
|
module Uniword
|
|
6
6
|
module Properties
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Helper to define val= override after attribute declaration.
|
|
21
|
-
# Uses alias_method to save the generated setter, then overrides it.
|
|
22
|
-
module BooleanValSetter
|
|
23
|
-
def self.included(base)
|
|
24
|
-
base.class_eval do
|
|
25
|
-
# Save the generated val= method
|
|
26
|
-
alias_method :__original_val_setter=, :val= if method_defined?(:val=)
|
|
27
|
-
# Override val= with our custom logic
|
|
28
|
-
define_method(:val=) do |v|
|
|
29
|
-
@val = if v.nil?
|
|
30
|
-
nil
|
|
31
|
-
elsif v == false || v.to_s == "false"
|
|
32
|
-
"false"
|
|
33
|
-
elsif v == true || v.to_s == "true"
|
|
34
|
-
nil
|
|
35
|
-
else
|
|
36
|
-
v
|
|
37
|
-
end
|
|
38
|
-
value_set_for(:val) if @val
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Strike-through formatting
|
|
45
|
-
class Strike < Lutaml::Model::Serializable
|
|
46
|
-
include BooleanElement
|
|
47
|
-
|
|
48
|
-
attribute :val, :string, default: nil
|
|
49
|
-
include BooleanValSetter
|
|
50
|
-
|
|
51
|
-
xml do
|
|
52
|
-
element "strike"
|
|
53
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
54
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Double strike-through formatting
|
|
59
|
-
class DoubleStrike < Lutaml::Model::Serializable
|
|
60
|
-
include BooleanElement
|
|
61
|
-
|
|
62
|
-
attribute :val, :string, default: nil
|
|
63
|
-
include BooleanValSetter
|
|
64
|
-
|
|
65
|
-
xml do
|
|
66
|
-
element "dstrike"
|
|
67
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
68
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Small caps formatting
|
|
73
|
-
class SmallCaps < Lutaml::Model::Serializable
|
|
74
|
-
include BooleanElement
|
|
75
|
-
|
|
76
|
-
attribute :val, :string, default: nil
|
|
77
|
-
include BooleanValSetter
|
|
78
|
-
|
|
79
|
-
xml do
|
|
80
|
-
element "smallCaps"
|
|
81
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
82
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# All caps formatting
|
|
87
|
-
class Caps < Lutaml::Model::Serializable
|
|
88
|
-
include BooleanElement
|
|
89
|
-
|
|
90
|
-
attribute :val, :string, default: nil
|
|
91
|
-
include BooleanValSetter
|
|
92
|
-
|
|
93
|
-
xml do
|
|
94
|
-
element "caps"
|
|
95
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
96
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Hidden text
|
|
101
|
-
class Vanish < Lutaml::Model::Serializable
|
|
102
|
-
include BooleanElement
|
|
103
|
-
|
|
104
|
-
attribute :val, :string, default: nil
|
|
105
|
-
include BooleanValSetter
|
|
106
|
-
|
|
107
|
-
xml do
|
|
108
|
-
element "vanish"
|
|
109
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
110
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Web hidden text (hidden from web view)
|
|
115
|
-
class WebHidden < Lutaml::Model::Serializable
|
|
116
|
-
include BooleanElement
|
|
117
|
-
|
|
118
|
-
attribute :val, :string, default: nil
|
|
119
|
-
include BooleanValSetter
|
|
120
|
-
|
|
121
|
-
xml do
|
|
122
|
-
element "webHidden"
|
|
123
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
124
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# No proofing (disable spell/grammar check)
|
|
129
|
-
class NoProof < Lutaml::Model::Serializable
|
|
130
|
-
include BooleanElement
|
|
131
|
-
|
|
132
|
-
attribute :val, :string, default: nil
|
|
133
|
-
include BooleanValSetter
|
|
134
|
-
|
|
135
|
-
xml do
|
|
136
|
-
element "noProof"
|
|
137
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
138
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Shadow formatting
|
|
143
|
-
class Shadow < Lutaml::Model::Serializable
|
|
144
|
-
include BooleanElement
|
|
145
|
-
|
|
146
|
-
attribute :val, :string, default: nil
|
|
147
|
-
include BooleanValSetter
|
|
148
|
-
|
|
149
|
-
xml do
|
|
150
|
-
element "shadow"
|
|
151
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
152
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Emboss formatting
|
|
157
|
-
class Emboss < Lutaml::Model::Serializable
|
|
158
|
-
include BooleanElement
|
|
159
|
-
|
|
160
|
-
attribute :val, :string, default: nil
|
|
161
|
-
include BooleanValSetter
|
|
162
|
-
|
|
163
|
-
xml do
|
|
164
|
-
element "emboss"
|
|
165
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
166
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Imprint formatting
|
|
171
|
-
class Imprint < Lutaml::Model::Serializable
|
|
172
|
-
include BooleanElement
|
|
173
|
-
|
|
174
|
-
attribute :val, :string, default: nil
|
|
175
|
-
include BooleanValSetter
|
|
176
|
-
|
|
177
|
-
xml do
|
|
178
|
-
element "imprint"
|
|
179
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
180
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
181
|
-
end
|
|
182
|
-
end
|
|
7
|
+
# Run-level boolean formatting elements
|
|
8
|
+
BooleanElementFactory.define("strike", "Strike")
|
|
9
|
+
BooleanElementFactory.define("dstrike", "DoubleStrike")
|
|
10
|
+
BooleanElementFactory.define("smallCaps", "SmallCaps")
|
|
11
|
+
BooleanElementFactory.define("caps", "Caps")
|
|
12
|
+
BooleanElementFactory.define("vanish", "Vanish")
|
|
13
|
+
BooleanElementFactory.define("webHidden", "WebHidden")
|
|
14
|
+
BooleanElementFactory.define("noProof", "NoProof")
|
|
15
|
+
BooleanElementFactory.define("shadow", "Shadow")
|
|
16
|
+
BooleanElementFactory.define("emboss", "Emboss")
|
|
17
|
+
BooleanElementFactory.define("imprint", "Imprint")
|
|
183
18
|
|
|
184
19
|
# Style-level boolean elements
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
include BooleanElement
|
|
189
|
-
|
|
190
|
-
attribute :val, :string, default: nil
|
|
191
|
-
include BooleanValSetter
|
|
192
|
-
|
|
193
|
-
xml do
|
|
194
|
-
element "qFormat"
|
|
195
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
196
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# Keep with next paragraph
|
|
201
|
-
class KeepNext < Lutaml::Model::Serializable
|
|
202
|
-
include BooleanElement
|
|
203
|
-
|
|
204
|
-
attribute :val, :string, default: nil
|
|
205
|
-
include BooleanValSetter
|
|
206
|
-
|
|
207
|
-
xml do
|
|
208
|
-
element "keepNext"
|
|
209
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
210
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# Keep lines together
|
|
215
|
-
class KeepLines < Lutaml::Model::Serializable
|
|
216
|
-
include BooleanElement
|
|
217
|
-
|
|
218
|
-
attribute :val, :string, default: nil
|
|
219
|
-
include BooleanValSetter
|
|
220
|
-
|
|
221
|
-
xml do
|
|
222
|
-
element "keepLines"
|
|
223
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
224
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
225
|
-
end
|
|
226
|
-
end
|
|
20
|
+
BooleanElementFactory.define("qFormat", "QuickFormat")
|
|
21
|
+
BooleanElementFactory.define("keepNext", "KeepNext")
|
|
22
|
+
BooleanElementFactory.define("keepLines", "KeepLines")
|
|
227
23
|
end
|
|
228
24
|
end
|
|
@@ -1,40 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "boolean_element_factory"
|
|
4
4
|
|
|
5
5
|
module Uniword
|
|
6
6
|
module Properties
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# OOXML: <w:i/> means italic=true (absence of val= means true)
|
|
10
|
-
# <w:i w:val="false"/> means italic=false
|
|
11
|
-
#
|
|
12
|
-
# Model: nil = true (no val attribute), 'false' = explicit false
|
|
13
|
-
class Italic < Lutaml::Model::Serializable
|
|
14
|
-
include Uniword::Properties::BooleanElement
|
|
15
|
-
|
|
16
|
-
attribute :val, :string, default: nil
|
|
17
|
-
include Uniword::Properties::BooleanValSetter
|
|
18
|
-
|
|
19
|
-
xml do
|
|
20
|
-
element "i"
|
|
21
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
22
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Complex script italic
|
|
27
|
-
class ItalicCs < Lutaml::Model::Serializable
|
|
28
|
-
include Uniword::Properties::BooleanElement
|
|
29
|
-
|
|
30
|
-
attribute :val, :string, default: nil
|
|
31
|
-
include Uniword::Properties::BooleanValSetter
|
|
32
|
-
|
|
33
|
-
xml do
|
|
34
|
-
element "iCs"
|
|
35
|
-
namespace Uniword::Ooxml::Namespaces::WordProcessingML
|
|
36
|
-
map_attribute "val", to: :val, render_nil: false, render_default: false
|
|
37
|
-
end
|
|
38
|
-
end
|
|
7
|
+
BooleanElementFactory.define("i", "Italic")
|
|
8
|
+
BooleanElementFactory.define("iCs", "ItalicCs")
|
|
39
9
|
end
|
|
40
10
|
end
|
data/lib/uniword/properties.rb
CHANGED
|
@@ -23,10 +23,13 @@ module Uniword
|
|
|
23
23
|
autoload :NumberingProperties, "uniword/properties/numbering_properties"
|
|
24
24
|
autoload :Tabs, "uniword/properties/tabs"
|
|
25
25
|
|
|
26
|
+
# Boolean element infrastructure
|
|
27
|
+
autoload :BooleanElement, "uniword/properties/boolean_element_factory"
|
|
28
|
+
autoload :BooleanValSetter, "uniword/properties/boolean_element_factory"
|
|
29
|
+
autoload :BooleanElementFactory,
|
|
30
|
+
"uniword/properties/boolean_element_factory"
|
|
31
|
+
|
|
26
32
|
# Boolean formatting classes (from boolean_formatting.rb)
|
|
27
|
-
# Note: BooleanElement and BooleanValSetter are also in boolean_formatting.rb
|
|
28
|
-
autoload :BooleanElement, "uniword/properties/boolean_formatting"
|
|
29
|
-
autoload :BooleanValSetter, "uniword/properties/boolean_formatting"
|
|
30
33
|
autoload :Strike, "uniword/properties/boolean_formatting"
|
|
31
34
|
autoload :DoubleStrike, "uniword/properties/boolean_formatting"
|
|
32
35
|
autoload :SmallCaps, "uniword/properties/boolean_formatting"
|
|
@@ -49,7 +52,9 @@ module Uniword
|
|
|
49
52
|
|
|
50
53
|
# Bold and Italic (from bold.rb and italic.rb)
|
|
51
54
|
autoload :Bold, "uniword/properties/bold"
|
|
55
|
+
autoload :BoldCs, "uniword/properties/bold"
|
|
52
56
|
autoload :Italic, "uniword/properties/italic"
|
|
57
|
+
autoload :ItalicCs, "uniword/properties/italic"
|
|
53
58
|
|
|
54
59
|
# Complex objects
|
|
55
60
|
autoload :Spacing, "uniword/properties/spacing"
|
|
@@ -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::HasRunPosition)
|
|
551
551
|
pos ||= fallback_pos
|
|
552
552
|
by_pos[pos] ||= []
|
|
553
553
|
by_pos[pos] << el
|
data/lib/uniword/version.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
4
|
|
|
5
|
+
require_relative "../document_input"
|
|
5
6
|
require_relative "document_root/feature_facade"
|
|
6
7
|
|
|
7
8
|
module Uniword
|
|
@@ -12,6 +13,7 @@ module Uniword
|
|
|
12
13
|
# Element: <w:document>
|
|
13
14
|
class DocumentRoot < Lutaml::Model::Serializable
|
|
14
15
|
include FeatureFacade
|
|
16
|
+
include Uniword::DocumentInput
|
|
15
17
|
|
|
16
18
|
attribute :mc_ignorable, Uniword::Ooxml::Types::McIgnorable
|
|
17
19
|
attribute :body, Body, default: -> { Body.new }
|
|
@@ -210,6 +212,15 @@ module Uniword
|
|
|
210
212
|
end.compact
|
|
211
213
|
end
|
|
212
214
|
|
|
215
|
+
# @return [Hash] Document statistics (paragraphs, tables, images)
|
|
216
|
+
def document_stats
|
|
217
|
+
{
|
|
218
|
+
paragraphs: paragraphs.count,
|
|
219
|
+
tables: tables.count,
|
|
220
|
+
images: images.count,
|
|
221
|
+
}
|
|
222
|
+
end
|
|
223
|
+
|
|
213
224
|
# Check if document structure is valid.
|
|
214
225
|
# Runs structural checks via Validation::StructuralValidator.
|
|
215
226
|
# Use the verify CLI command for full OPC + XSD + semantic validation.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
|
+
require_relative "../has_run_position"
|
|
4
5
|
|
|
5
6
|
module Uniword
|
|
6
7
|
module Wordprocessingml
|
|
@@ -9,12 +10,17 @@ module Uniword
|
|
|
9
10
|
# Generated from OOXML schema: wordprocessingml.yml
|
|
10
11
|
# Element: <w:hyperlink>
|
|
11
12
|
class Hyperlink < Lutaml::Model::Serializable
|
|
13
|
+
include Uniword::HasRunPosition
|
|
14
|
+
|
|
12
15
|
attribute :id, :string
|
|
13
16
|
attribute :anchor, :string
|
|
14
17
|
attribute :tooltip, :string
|
|
15
18
|
attribute :history, Uniword::Properties::HistoryValue
|
|
16
19
|
attribute :runs, Run, collection: true, initialize_empty: true
|
|
17
20
|
|
|
21
|
+
# Transient: position among sibling runs (used by MHTML renderer)
|
|
22
|
+
attribute :run_position, :integer
|
|
23
|
+
|
|
18
24
|
xml do
|
|
19
25
|
element "hyperlink"
|
|
20
26
|
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.9
|
|
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-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: logger
|
|
@@ -771,6 +771,7 @@ files:
|
|
|
771
771
|
- lib/uniword/diff/package_diff_result.rb
|
|
772
772
|
- lib/uniword/diff/package_differ.rb
|
|
773
773
|
- lib/uniword/document_factory.rb
|
|
774
|
+
- lib/uniword/document_input.rb
|
|
774
775
|
- lib/uniword/document_variables.rb
|
|
775
776
|
- lib/uniword/document_variables/data_type.rb
|
|
776
777
|
- lib/uniword/document_variables/default_value.rb
|
|
@@ -966,6 +967,7 @@ files:
|
|
|
966
967
|
- lib/uniword/glossary/glossary_document.rb
|
|
967
968
|
- lib/uniword/glossary/style_id.rb
|
|
968
969
|
- lib/uniword/glossary/text_box.rb
|
|
970
|
+
- lib/uniword/has_run_position.rb
|
|
969
971
|
- lib/uniword/header.rb
|
|
970
972
|
- lib/uniword/headers_footers.rb
|
|
971
973
|
- lib/uniword/headers_footers/manager.rb
|
|
@@ -1157,7 +1159,6 @@ files:
|
|
|
1157
1159
|
- lib/uniword/ooxml/types/dcterms_modified_type.rb
|
|
1158
1160
|
- lib/uniword/ooxml/types/dcterms_w3cdtf_type.rb
|
|
1159
1161
|
- lib/uniword/ooxml/types/mc_ignorable_type.rb
|
|
1160
|
-
- lib/uniword/ooxml/types/on_off_type.rb
|
|
1161
1162
|
- lib/uniword/ooxml/types/ooxml_boolean.rb
|
|
1162
1163
|
- lib/uniword/ooxml/types/ooxml_boolean_optional.rb
|
|
1163
1164
|
- lib/uniword/ooxml/types/relationship_id.rb
|
|
@@ -1232,6 +1233,7 @@ files:
|
|
|
1232
1233
|
- lib/uniword/properties/auto_space_de.rb
|
|
1233
1234
|
- lib/uniword/properties/auto_space_dn.rb
|
|
1234
1235
|
- lib/uniword/properties/bold.rb
|
|
1236
|
+
- lib/uniword/properties/boolean_element_factory.rb
|
|
1235
1237
|
- lib/uniword/properties/boolean_formatting.rb
|
|
1236
1238
|
- lib/uniword/properties/border.rb
|
|
1237
1239
|
- lib/uniword/properties/borders.rb
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "lutaml/model"
|
|
4
|
-
|
|
5
|
-
module Uniword
|
|
6
|
-
module Ooxml
|
|
7
|
-
module Types
|
|
8
|
-
# OOXML On/Off (boolean) type for attributes.
|
|
9
|
-
#
|
|
10
|
-
# OOXML uses "0" and "1" for boolean attribute values, not "false"/"true".
|
|
11
|
-
# When an attribute is not present (nil), it should remain nil, not be
|
|
12
|
-
# serialized as "0". This preserves the original document structure.
|
|
13
|
-
#
|
|
14
|
-
# @example Serialize a boolean attribute
|
|
15
|
-
# # Ruby true → XML "1"
|
|
16
|
-
# # Ruby false → XML "0"
|
|
17
|
-
# # Ruby nil → attribute not rendered
|
|
18
|
-
class OnOffType < Lutaml::Model::Type::String
|
|
19
|
-
# Bypass parent's initialize to avoid infinite recursion.
|
|
20
|
-
# Parent calls self.class.cast(value) which calls self.new(value).
|
|
21
|
-
def initialize(value)
|
|
22
|
-
@value = value
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Cast value to OnOffType instance (for attribute serialization).
|
|
26
|
-
# nil remains nil (not present in source), preserving original structure.
|
|
27
|
-
# Already-wrapped OnOffType instances are returned as-is.
|
|
28
|
-
def self.cast(value, _options = {})
|
|
29
|
-
return nil if value.nil?
|
|
30
|
-
return value if value.is_a?(OnOffType)
|
|
31
|
-
|
|
32
|
-
raw = case value
|
|
33
|
-
when true, "true", "1", 1
|
|
34
|
-
true
|
|
35
|
-
when false, "false", "0", 0
|
|
36
|
-
false
|
|
37
|
-
else
|
|
38
|
-
false
|
|
39
|
-
end
|
|
40
|
-
new(raw)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Convert stored boolean to OOXML "1" or "0"
|
|
44
|
-
def to_xml
|
|
45
|
-
return nil if @value.nil?
|
|
46
|
-
|
|
47
|
-
@value == true ? "1" : "0"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Fallback to_s also returns "1"/"0" for compatibility
|
|
51
|
-
def to_s
|
|
52
|
-
return "" if @value.nil?
|
|
53
|
-
|
|
54
|
-
to_xml
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Return the raw boolean value for Ruby comparisons
|
|
58
|
-
def to_bool
|
|
59
|
-
@value == true
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|