uniword 1.2.5 → 1.3.0
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/CHANGELOG.md +223 -0
- data/CONTRIBUTING.md +1 -1
- data/config/ooxml/schemas/shared_types.yml +5 -4
- data/data/schemas/ecma/dc.xsd +118 -0
- data/data/schemas/ecma/dcmitype.xsd +50 -0
- data/data/schemas/ecma/dcterms.xsd +331 -0
- data/data/schemas/ecma/opc-coreProperties.xsd +2 -2
- data/data/schemas/ecma/xml.xsd +117 -0
- data/lib/uniword/builder/bibliography_builder.rb +1 -1
- data/lib/uniword/builder/chart_builder.rb +19 -9
- data/lib/uniword/builder/comment_anchorer.rb +221 -0
- data/lib/uniword/builder/document_builder.rb +97 -6
- data/lib/uniword/builder/image_builder.rb +6 -5
- data/lib/uniword/builder/run_utils.rb +2 -0
- data/lib/uniword/builder.rb +1 -0
- data/lib/uniword/cli/fonts_cli.rb +52 -0
- data/lib/uniword/cli/main.rb +110 -11
- data/lib/uniword/cli/page_cli.rb +85 -0
- data/lib/uniword/cli/styles_cli.rb +204 -0
- data/lib/uniword/cli/theme_cli.rb +88 -0
- data/lib/uniword/cli/toc_cli.rb +22 -2
- data/lib/uniword/comment.rb +20 -15
- data/lib/uniword/comment_range.rb +3 -3
- data/lib/uniword/comments_part.rb +51 -19
- data/lib/uniword/configuration/configuration_loader.rb +5 -3
- data/lib/uniword/configuration.rb +93 -1
- data/lib/uniword/content_types.rb +19 -39
- data/lib/uniword/document_factory.rb +9 -1
- data/lib/uniword/document_writer.rb +16 -5
- data/lib/uniword/docx/chart_part.rb +34 -0
- data/lib/uniword/docx/custom_xml_item.rb +83 -0
- data/lib/uniword/docx/header_footer_part.rb +140 -0
- data/lib/uniword/docx/header_footer_part_collection.rb +135 -0
- data/lib/uniword/docx/header_footer_view.rb +225 -0
- data/lib/uniword/docx/id_allocator.rb +130 -36
- data/lib/uniword/docx/package.rb +223 -35
- data/lib/uniword/docx/package_defaults.rb +58 -78
- data/lib/uniword/docx/package_integrity_checker.rb +312 -0
- data/lib/uniword/docx/package_serialization.rb +103 -231
- data/lib/uniword/docx/part.rb +106 -0
- data/lib/uniword/docx/part_collection.rb +123 -0
- data/lib/uniword/docx/reconciler/body.rb +59 -68
- data/lib/uniword/docx/reconciler/fix.rb +46 -0
- data/lib/uniword/docx/reconciler/fix_codes.rb +37 -30
- data/lib/uniword/docx/reconciler/helpers.rb +9 -1
- data/lib/uniword/docx/reconciler/notes.rb +17 -8
- data/lib/uniword/docx/reconciler/package_structure.rb +169 -221
- data/lib/uniword/docx/reconciler/parts.rb +36 -13
- data/lib/uniword/docx/reconciler/referential_integrity.rb +297 -123
- data/lib/uniword/docx/reconciler/tables.rb +78 -16
- data/lib/uniword/docx/reconciler/theme.rb +7 -4
- data/lib/uniword/docx/reconciler.rb +36 -14
- data/lib/uniword/docx.rb +10 -0
- data/lib/uniword/drawingml/blip.rb +0 -1
- data/lib/uniword/drawingml/color_scheme.rb +5 -6
- data/lib/uniword/drawingml/font_scheme.rb +4 -8
- data/lib/uniword/drawingml/theme.rb +12 -6
- data/lib/uniword/errors.rb +8 -1
- data/lib/uniword/footer.rb +4 -0
- data/lib/uniword/header.rb +4 -0
- data/lib/uniword/hyperlink.rb +1 -1
- data/lib/uniword/image.rb +6 -6
- data/lib/uniword/lazy_loader.rb +1 -2
- data/lib/uniword/model_attribute_access.rb +20 -4
- data/lib/uniword/ooxml/dotx_package.rb +42 -2
- data/lib/uniword/ooxml/part_definition.rb +171 -0
- data/lib/uniword/ooxml/part_registry.rb +311 -0
- data/lib/uniword/ooxml/relationships/image_relationship.rb +1 -1
- data/lib/uniword/ooxml/relationships/package_relationships.rb +20 -22
- data/lib/uniword/ooxml/schema/element_serializer.rb +8 -7
- data/lib/uniword/ooxml/types/hex_color_value.rb +38 -0
- data/lib/uniword/ooxml/types/ooxml_boolean.rb +27 -14
- data/lib/uniword/ooxml/types/ooxml_boolean_optional.rb +21 -16
- data/lib/uniword/ooxml/types/theme_color_value.rb +22 -0
- data/lib/uniword/ooxml/types/unsigned_decimal_number.rb +30 -0
- data/lib/uniword/ooxml/types.rb +10 -0
- data/lib/uniword/ooxml.rb +2 -0
- data/lib/uniword/properties/alignment.rb +8 -3
- data/lib/uniword/properties/border.rb +52 -6
- data/lib/uniword/properties/cell_vertical_align.rb +4 -1
- data/lib/uniword/properties/color_value.rb +8 -3
- data/lib/uniword/properties/highlight.rb +9 -5
- data/lib/uniword/properties/shading.rb +4 -3
- data/lib/uniword/properties/tab_stop.rb +6 -1
- data/lib/uniword/properties/table_justification.rb +4 -1
- data/lib/uniword/properties/underline.rb +4 -2
- data/lib/uniword/properties/vertical_align.rb +6 -3
- data/lib/uniword/review/review_manager.rb +23 -9
- data/lib/uniword/revision.rb +9 -9
- data/lib/uniword/schema/model_generator.rb +31 -15
- data/lib/uniword/shared_types/hex_color.rb +4 -1
- data/lib/uniword/shared_types/pixel_measure.rb +4 -1
- data/lib/uniword/shared_types/point_measure.rb +4 -1
- data/lib/uniword/shared_types/text_alignment.rb +6 -1
- data/lib/uniword/shared_types/twips_measure.rb +4 -1
- data/lib/uniword/spreadsheetml/phonetic_pr.rb +1 -1
- data/lib/uniword/spreadsheetml/shared_string_table.rb +2 -2
- data/lib/uniword/spreadsheetml/table_formula.rb +1 -1
- data/lib/uniword/template/variable_resolver.rb +40 -7
- data/lib/uniword/themes/theme_transformation.rb +55 -41
- data/lib/uniword/toc/toc_generator.rb +2 -0
- data/lib/uniword/validation/engine.rb +35 -0
- data/lib/uniword/validation/link_checker.rb +1 -1
- data/lib/uniword/validation/opc_validator.rb +0 -1
- data/lib/uniword/validation/report/layer_result.rb +0 -1
- data/lib/uniword/validation/report/terminal_formatter.rb +0 -1
- data/lib/uniword/validation/report/verification_report.rb +0 -2
- data/lib/uniword/validation/report.rb +20 -0
- data/lib/uniword/validation/rules/base.rb +10 -2
- data/lib/uniword/validation/rules/bookmark_pairing_rule.rb +38 -0
- data/lib/uniword/validation/rules/bookmark_uniqueness_rule.rb +48 -0
- data/lib/uniword/validation/rules/bookmarks_rule.rb +0 -2
- data/lib/uniword/validation/rules/content_types_coverage_rule.rb +0 -2
- data/lib/uniword/validation/rules/core_properties_namespace_rule.rb +0 -2
- data/lib/uniword/validation/rules/document_body_rule.rb +25 -0
- data/lib/uniword/validation/rules/document_context.rb +7 -0
- data/lib/uniword/validation/rules/empty_paragraphs_rule.rb +27 -0
- data/lib/uniword/validation/rules/font_table_signature_rule.rb +0 -2
- data/lib/uniword/validation/rules/fonts_rule.rb +0 -2
- data/lib/uniword/validation/rules/footnotes_rule.rb +0 -2
- data/lib/uniword/validation/rules/headers_footers_rule.rb +0 -2
- data/lib/uniword/validation/rules/images_rule.rb +0 -2
- data/lib/uniword/validation/rules/mc_ignorable_namespace_rule.rb +0 -2
- data/lib/uniword/validation/rules/model_context.rb +32 -0
- data/lib/uniword/validation/rules/model_rule.rb +34 -0
- data/lib/uniword/validation/rules/numbering_preservation_rule.rb +0 -2
- data/lib/uniword/validation/rules/numbering_rule.rb +0 -2
- data/lib/uniword/validation/rules/relationship_integrity_rule.rb +0 -1
- data/lib/uniword/validation/rules/rsid_rule.rb +0 -2
- data/lib/uniword/validation/rules/section_properties_rule.rb +0 -2
- data/lib/uniword/validation/rules/settings_rule.rb +0 -2
- data/lib/uniword/validation/rules/settings_values_rule.rb +0 -2
- data/lib/uniword/validation/rules/style_references_rule.rb +0 -2
- data/lib/uniword/validation/rules/table_grid_rule.rb +27 -0
- data/lib/uniword/validation/rules/table_properties_rule.rb +27 -0
- data/lib/uniword/validation/rules/tables_rule.rb +0 -2
- data/lib/uniword/validation/rules/theme_completeness_rule.rb +0 -2
- data/lib/uniword/validation/rules/theme_rule.rb +0 -2
- data/lib/uniword/validation/rules.rb +61 -23
- data/lib/uniword/validation/schema_registry.rb +11 -0
- data/lib/uniword/validation/validators/document_semantics_validator.rb +1 -9
- data/lib/uniword/validation/validators/xml_schema_validator.rb +0 -2
- data/lib/uniword/validation/validators.rb +0 -9
- data/lib/uniword/validation/verify_orchestrator.rb +0 -7
- data/lib/uniword/validation.rb +7 -2
- data/lib/uniword/version.rb +1 -1
- data/lib/uniword/vml/imagedata.rb +0 -1
- data/lib/uniword/wordprocessingml/attached_template.rb +0 -1
- data/lib/uniword/wordprocessingml/deleted_text.rb +1 -1
- data/lib/uniword/wordprocessingml/document_root.rb +211 -21
- data/lib/uniword/wordprocessingml/endnotes.rb +4 -0
- data/lib/uniword/wordprocessingml/font_replacer.rb +147 -0
- data/lib/uniword/wordprocessingml/footnotes.rb +4 -0
- data/lib/uniword/wordprocessingml/hdr_shape_defaults.rb +0 -1
- data/lib/uniword/wordprocessingml/hyperlink.rb +0 -1
- data/lib/uniword/wordprocessingml/level.rb +6 -20
- data/lib/uniword/wordprocessingml/math_pr.rb +0 -11
- data/lib/uniword/wordprocessingml/numbering_definition.rb +16 -8
- data/lib/uniword/wordprocessingml/page_setup.rb +181 -0
- data/lib/uniword/wordprocessingml/paragraph_properties.rb +33 -28
- data/lib/uniword/wordprocessingml/rsids.rb +0 -2
- data/lib/uniword/wordprocessingml/run.rb +3 -0
- data/lib/uniword/wordprocessingml/section_properties.rb +2 -2
- data/lib/uniword/wordprocessingml/settings.rb +8 -34
- data/lib/uniword/wordprocessingml/shape_defaults.rb +0 -1
- data/lib/uniword/wordprocessingml/style.rb +2 -1
- data/lib/uniword/wordprocessingml/style_cleanup.rb +198 -0
- data/lib/uniword/wordprocessingml/table_cell_properties.rb +6 -6
- data/lib/uniword/wordprocessingml/update_fields.rb +27 -0
- data/lib/uniword/wordprocessingml/w14_attributes.rb +30 -2
- data/lib/uniword/wordprocessingml.rb +4 -0
- data/lib/uniword.rb +27 -2
- metadata +38 -19
- data/config/validation_rules.yml +0 -60
- data/config/warning_rules.yml +0 -57
- data/lib/uniword/validation/document_validator.rb +0 -272
- data/lib/uniword/validation/structural_validator.rb +0 -116
- data/lib/uniword/validation/validators/content_type_validator.rb +0 -165
- data/lib/uniword/validation/validators/file_structure_validator.rb +0 -104
- data/lib/uniword/validation/validators/ooxml_part_validator.rb +0 -128
- data/lib/uniword/validation/validators/relationship_validator.rb +0 -147
- data/lib/uniword/validation/validators/zip_integrity_validator.rb +0 -110
- data/lib/uniword/validators/element_validator.rb +0 -93
- data/lib/uniword/validators/paragraph_validator.rb +0 -116
- data/lib/uniword/validators/table_validator.rb +0 -134
- data/lib/uniword/validators.rb +0 -9
- data/lib/uniword/warnings/warning.rb +0 -130
- data/lib/uniword/warnings/warning_collector.rb +0 -234
- data/lib/uniword/warnings/warning_report.rb +0 -159
- data/lib/uniword/warnings.rb +0 -9
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Uniword
|
|
4
|
+
module Ooxml
|
|
5
|
+
# Declarative description of one OOXML package part kind.
|
|
6
|
+
#
|
|
7
|
+
# A PartDefinition is the single source of truth for a part's
|
|
8
|
+
# package path, content type, relationship type, and the form its
|
|
9
|
+
# [Content_Types].xml entry takes (Default by extension or Override
|
|
10
|
+
# by part name). Instances are immutable value objects registered
|
|
11
|
+
# in {PartRegistry}; consumers derive every literal from them.
|
|
12
|
+
#
|
|
13
|
+
# Parts with instance-numbered paths (headers, footers, charts,
|
|
14
|
+
# customXml items) carry a +path_pattern+/+target_pattern+ with
|
|
15
|
+
# +format+-style placeholders (e.g. "word/header%<counter>d.xml")
|
|
16
|
+
# instead of a fixed path.
|
|
17
|
+
#
|
|
18
|
+
# @example Fixed part
|
|
19
|
+
# PartDefinition.new(
|
|
20
|
+
# key: :styles, path: "word/styles.xml", target: "styles.xml",
|
|
21
|
+
# content_type: "...styles+xml",
|
|
22
|
+
# rel_type: ".../relationships/styles",
|
|
23
|
+
# required: true, kind: :override, rels_scope: :document,
|
|
24
|
+
# )
|
|
25
|
+
class PartDefinition
|
|
26
|
+
# @return [Symbol] unique registry key (e.g. :styles)
|
|
27
|
+
attr_reader :key
|
|
28
|
+
|
|
29
|
+
# @return [String, nil] package-relative path ("word/styles.xml")
|
|
30
|
+
attr_reader :path
|
|
31
|
+
|
|
32
|
+
# @return [String, nil] path template for instance-numbered parts
|
|
33
|
+
attr_reader :path_pattern
|
|
34
|
+
|
|
35
|
+
# @return [String, nil] relationship Target as written in .rels
|
|
36
|
+
# (relative to the owning part; defaults to +path+)
|
|
37
|
+
attr_reader :target
|
|
38
|
+
|
|
39
|
+
# @return [String, nil] Target template for numbered parts
|
|
40
|
+
attr_reader :target_pattern
|
|
41
|
+
|
|
42
|
+
# @return [String, nil] MIME content type (nil when dynamic,
|
|
43
|
+
# e.g. images whose type depends on the file extension)
|
|
44
|
+
attr_reader :content_type
|
|
45
|
+
|
|
46
|
+
# @return [String, nil] relationship type URI (nil when the part
|
|
47
|
+
# is not referenced by a relationship)
|
|
48
|
+
attr_reader :rel_type
|
|
49
|
+
|
|
50
|
+
# @return [String, nil] file extension for Default entries
|
|
51
|
+
attr_reader :extension
|
|
52
|
+
|
|
53
|
+
# @return [Boolean] whether a minimal valid package carries it
|
|
54
|
+
attr_reader :required
|
|
55
|
+
|
|
56
|
+
# @return [Symbol] :override, :default, or :none (no
|
|
57
|
+
# [Content_Types].xml entry of its own)
|
|
58
|
+
attr_reader :kind
|
|
59
|
+
|
|
60
|
+
# @return [Symbol, nil] :package or :document — which .rels part
|
|
61
|
+
# the relationship lives in (nil when +rel_type+ is nil)
|
|
62
|
+
attr_reader :rels_scope
|
|
63
|
+
|
|
64
|
+
# @return [Boolean] whether ContentTypes.generate includes it in
|
|
65
|
+
# the comprehensive [Content_Types].xml for new DOCX packages
|
|
66
|
+
attr_reader :standard
|
|
67
|
+
|
|
68
|
+
# rubocop:disable Metrics/ParameterLists
|
|
69
|
+
def initialize(key:, kind:, path: nil, path_pattern: nil,
|
|
70
|
+
target: nil, target_pattern: nil,
|
|
71
|
+
content_type: nil, rel_type: nil, extension: nil,
|
|
72
|
+
required: false, rels_scope: nil, standard: false)
|
|
73
|
+
@key = key.to_sym
|
|
74
|
+
@kind = kind
|
|
75
|
+
@path = path
|
|
76
|
+
@path_pattern = path_pattern
|
|
77
|
+
@target = target || path
|
|
78
|
+
@target_pattern = target_pattern
|
|
79
|
+
@content_type = content_type
|
|
80
|
+
@rel_type = rel_type
|
|
81
|
+
@extension = extension
|
|
82
|
+
@required = required
|
|
83
|
+
@rels_scope = rels_scope
|
|
84
|
+
@standard = standard
|
|
85
|
+
end
|
|
86
|
+
# rubocop:enable Metrics/ParameterLists
|
|
87
|
+
|
|
88
|
+
# @return [Boolean] true for [Content_Types].xml Override entries
|
|
89
|
+
def override?
|
|
90
|
+
kind == :override
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @return [Boolean] true for [Content_Types].xml Default entries
|
|
94
|
+
def default?
|
|
95
|
+
kind == :default
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @return [Boolean] true when a minimal valid package carries it
|
|
99
|
+
def required?
|
|
100
|
+
!!required
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @return [Boolean] true when ContentTypes.generate includes it
|
|
104
|
+
def standard?
|
|
105
|
+
!!standard
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @return [Boolean] true when the relationship lives in _rels/.rels
|
|
109
|
+
def package_rel?
|
|
110
|
+
rels_scope == :package
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Resolve the package-relative path, interpolating pattern
|
|
114
|
+
# placeholders for instance-numbered parts.
|
|
115
|
+
#
|
|
116
|
+
# @param vars [Hash] placeholder values (e.g. counter: 1)
|
|
117
|
+
# @return [String, nil] e.g. "word/header1.xml"
|
|
118
|
+
def path_for(**vars)
|
|
119
|
+
expand(path_pattern || path, vars)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Resolve the content-type Override part name (leading slash).
|
|
123
|
+
#
|
|
124
|
+
# @param vars [Hash] placeholder values for numbered parts
|
|
125
|
+
# @return [String, nil] e.g. "/word/header1.xml"
|
|
126
|
+
def part_name_for(**vars)
|
|
127
|
+
resolved = path_for(**vars)
|
|
128
|
+
resolved && "/#{resolved}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Resolve the relationship Target attribute.
|
|
132
|
+
#
|
|
133
|
+
# @param vars [Hash] placeholder values for numbered parts
|
|
134
|
+
# @return [String, nil] e.g. "header1.xml"
|
|
135
|
+
def target_for(**vars)
|
|
136
|
+
expand(target_pattern || target, vars)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Fixed-path Override part name (leading slash).
|
|
140
|
+
#
|
|
141
|
+
# @return [String, nil] e.g. "/word/styles.xml"
|
|
142
|
+
def part_name
|
|
143
|
+
path && "/#{path}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Two definitions are equal when every field matches.
|
|
147
|
+
def ==(other)
|
|
148
|
+
other.is_a?(PartDefinition) && fields == other.fields
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
protected
|
|
152
|
+
|
|
153
|
+
# @return [Array] field values in declaration order, used for
|
|
154
|
+
# value equality
|
|
155
|
+
def fields
|
|
156
|
+
[key, kind, path, path_pattern, target, target_pattern,
|
|
157
|
+
content_type, rel_type, extension, required, rels_scope,
|
|
158
|
+
standard]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
def expand(template, vars)
|
|
164
|
+
return nil unless template
|
|
165
|
+
return template if vars.empty?
|
|
166
|
+
|
|
167
|
+
format(template, vars)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Uniword
|
|
4
|
+
module Ooxml
|
|
5
|
+
# Single source of truth for OOXML package part metadata.
|
|
6
|
+
#
|
|
7
|
+
# Maps every part kind the library writes to its package path,
|
|
8
|
+
# content type, relationship type, and [Content_Types].xml entry
|
|
9
|
+
# form (Default vs Override). Content type and relationship type
|
|
10
|
+
# literals live ONLY here; consumers (Uniword::ContentTypes,
|
|
11
|
+
# Docx::PackageDefaults, Docx::Reconciler::PackageStructure, and
|
|
12
|
+
# the Docx::PackageSerialization inject_* methods) derive from
|
|
13
|
+
# this registry instead of holding their own literals.
|
|
14
|
+
#
|
|
15
|
+
# Open/closed: a new part kind is added by registering a
|
|
16
|
+
# PartDefinition — consumers need no further changes.
|
|
17
|
+
#
|
|
18
|
+
# @example Look up a part by key
|
|
19
|
+
# defn = PartRegistry.find_by_key(:styles)
|
|
20
|
+
# defn.part_name # => "/word/styles.xml"
|
|
21
|
+
# defn.rel_type # => ".../relationships/styles"
|
|
22
|
+
#
|
|
23
|
+
# @example Register a custom part kind
|
|
24
|
+
# PartRegistry.register(
|
|
25
|
+
# PartDefinition.new(key: :glossary, path: "word/glossary/document.xml",
|
|
26
|
+
# target: "glossary/document.xml",
|
|
27
|
+
# content_type: "...document.glossary+xml",
|
|
28
|
+
# rel_type: ".../glossaryDocument",
|
|
29
|
+
# kind: :override, rels_scope: :document)
|
|
30
|
+
# )
|
|
31
|
+
module PartRegistry
|
|
32
|
+
# Relationship type base URIs. These literals appear only here.
|
|
33
|
+
OFFICE_REL_BASE =
|
|
34
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
35
|
+
PACKAGE_REL_BASE =
|
|
36
|
+
"http://schemas.openxmlformats.org/package/2006/relationships"
|
|
37
|
+
|
|
38
|
+
# Content type prefixes (literals appear only here).
|
|
39
|
+
CT_OFFICE = "application/vnd.openxmlformats-officedocument"
|
|
40
|
+
CT_WML = "#{CT_OFFICE}.wordprocessingml".freeze
|
|
41
|
+
CT_PACKAGE = "application/vnd.openxmlformats-package"
|
|
42
|
+
|
|
43
|
+
# Built-in registrations. Registration order is observable in
|
|
44
|
+
# emitted output: the :standard entries reproduce the historic
|
|
45
|
+
# ContentTypes.generate ordering exactly (defaults first —
|
|
46
|
+
# jpeg, png, gif, rels, xml — then overrides in the historic
|
|
47
|
+
# order). Non-standard parts follow; consumers select their own
|
|
48
|
+
# ordered subsets by key.
|
|
49
|
+
BUILT_INS = [
|
|
50
|
+
# -- Content-type Defaults (extension → content type) --
|
|
51
|
+
{ key: :jpeg, kind: :default, extension: "jpeg",
|
|
52
|
+
content_type: "image/jpeg", standard: true },
|
|
53
|
+
{ key: :png, kind: :default, extension: "png",
|
|
54
|
+
content_type: "image/png", standard: true },
|
|
55
|
+
{ key: :gif, kind: :default, extension: "gif",
|
|
56
|
+
content_type: "image/gif", standard: true },
|
|
57
|
+
{ key: :rels, kind: :default, extension: "rels",
|
|
58
|
+
content_type: "#{CT_PACKAGE}.relationships+xml",
|
|
59
|
+
required: true, standard: true },
|
|
60
|
+
{ key: :xml, kind: :default, extension: "xml",
|
|
61
|
+
content_type: "application/xml",
|
|
62
|
+
required: true, standard: true },
|
|
63
|
+
|
|
64
|
+
# -- Standard Override parts (historic generate order) --
|
|
65
|
+
{ key: :document, kind: :override, path: "word/document.xml",
|
|
66
|
+
content_type: "#{CT_WML}.document.main+xml",
|
|
67
|
+
rel_type: "#{OFFICE_REL_BASE}/officeDocument",
|
|
68
|
+
required: true, standard: true, rels_scope: :package },
|
|
69
|
+
{ key: :numbering, kind: :override, path: "word/numbering.xml",
|
|
70
|
+
target: "numbering.xml", content_type: "#{CT_WML}.numbering+xml",
|
|
71
|
+
rel_type: "#{OFFICE_REL_BASE}/numbering",
|
|
72
|
+
standard: true, rels_scope: :document },
|
|
73
|
+
{ key: :styles, kind: :override, path: "word/styles.xml",
|
|
74
|
+
target: "styles.xml", content_type: "#{CT_WML}.styles+xml",
|
|
75
|
+
rel_type: "#{OFFICE_REL_BASE}/styles",
|
|
76
|
+
required: true, standard: true, rels_scope: :document },
|
|
77
|
+
{ key: :settings, kind: :override, path: "word/settings.xml",
|
|
78
|
+
target: "settings.xml", content_type: "#{CT_WML}.settings+xml",
|
|
79
|
+
rel_type: "#{OFFICE_REL_BASE}/settings",
|
|
80
|
+
required: true, standard: true, rels_scope: :document },
|
|
81
|
+
{ key: :web_settings, kind: :override, path: "word/webSettings.xml",
|
|
82
|
+
target: "webSettings.xml",
|
|
83
|
+
content_type: "#{CT_WML}.webSettings+xml",
|
|
84
|
+
rel_type: "#{OFFICE_REL_BASE}/webSettings",
|
|
85
|
+
required: true, standard: true, rels_scope: :document },
|
|
86
|
+
{ key: :font_table, kind: :override, path: "word/fontTable.xml",
|
|
87
|
+
target: "fontTable.xml",
|
|
88
|
+
content_type: "#{CT_WML}.fontTable+xml",
|
|
89
|
+
rel_type: "#{OFFICE_REL_BASE}/fontTable",
|
|
90
|
+
required: true, standard: true, rels_scope: :document },
|
|
91
|
+
{ key: :theme, kind: :override, path: "word/theme/theme1.xml",
|
|
92
|
+
target: "theme/theme1.xml",
|
|
93
|
+
content_type: "#{CT_OFFICE}.theme+xml",
|
|
94
|
+
rel_type: "#{OFFICE_REL_BASE}/theme",
|
|
95
|
+
standard: true, rels_scope: :document },
|
|
96
|
+
{ key: :core_properties, kind: :override, path: "docProps/core.xml",
|
|
97
|
+
content_type: "#{CT_PACKAGE}.core-properties+xml",
|
|
98
|
+
rel_type: "#{PACKAGE_REL_BASE}/metadata/core-properties",
|
|
99
|
+
required: true, standard: true, rels_scope: :package },
|
|
100
|
+
{ key: :app_properties, kind: :override, path: "docProps/app.xml",
|
|
101
|
+
content_type: "#{CT_OFFICE}.extended-properties+xml",
|
|
102
|
+
rel_type: "#{OFFICE_REL_BASE}/extended-properties",
|
|
103
|
+
required: true, standard: true, rels_scope: :package },
|
|
104
|
+
|
|
105
|
+
# -- Optional document parts --
|
|
106
|
+
{ key: :footnotes, kind: :override, path: "word/footnotes.xml",
|
|
107
|
+
target: "footnotes.xml",
|
|
108
|
+
content_type: "#{CT_WML}.footnotes+xml",
|
|
109
|
+
rel_type: "#{OFFICE_REL_BASE}/footnotes",
|
|
110
|
+
rels_scope: :document },
|
|
111
|
+
{ key: :endnotes, kind: :override, path: "word/endnotes.xml",
|
|
112
|
+
target: "endnotes.xml",
|
|
113
|
+
content_type: "#{CT_WML}.endnotes+xml",
|
|
114
|
+
rel_type: "#{OFFICE_REL_BASE}/endnotes",
|
|
115
|
+
rels_scope: :document },
|
|
116
|
+
{ key: :comments, kind: :override, path: "word/comments.xml",
|
|
117
|
+
target: "comments.xml",
|
|
118
|
+
content_type: "#{CT_WML}.comments+xml",
|
|
119
|
+
rel_type: "#{OFFICE_REL_BASE}/comments",
|
|
120
|
+
rels_scope: :document },
|
|
121
|
+
{ key: :bibliography, kind: :override, path: "word/sources.xml",
|
|
122
|
+
target: "sources.xml",
|
|
123
|
+
content_type: "#{CT_OFFICE}.bibliography+xml",
|
|
124
|
+
rel_type: "#{OFFICE_REL_BASE}/bibliography",
|
|
125
|
+
rels_scope: :document },
|
|
126
|
+
{ key: :header, kind: :override,
|
|
127
|
+
path_pattern: "word/header%<counter>d.xml",
|
|
128
|
+
target_pattern: "header%<counter>d.xml",
|
|
129
|
+
content_type: "#{CT_WML}.header+xml",
|
|
130
|
+
rel_type: "#{OFFICE_REL_BASE}/header",
|
|
131
|
+
rels_scope: :document },
|
|
132
|
+
{ key: :footer, kind: :override,
|
|
133
|
+
path_pattern: "word/footer%<counter>d.xml",
|
|
134
|
+
target_pattern: "footer%<counter>d.xml",
|
|
135
|
+
content_type: "#{CT_WML}.footer+xml",
|
|
136
|
+
rel_type: "#{OFFICE_REL_BASE}/footer",
|
|
137
|
+
rels_scope: :document },
|
|
138
|
+
{ key: :custom_properties, kind: :override,
|
|
139
|
+
path: "docProps/custom.xml",
|
|
140
|
+
content_type: "#{CT_OFFICE}.custom-properties+xml",
|
|
141
|
+
rel_type: "#{OFFICE_REL_BASE}/custom-properties",
|
|
142
|
+
rels_scope: :package },
|
|
143
|
+
{ key: :chart, kind: :override,
|
|
144
|
+
path_pattern: "word/charts/chart%<n>d.xml",
|
|
145
|
+
target_pattern: "charts/chart%<n>d.xml",
|
|
146
|
+
content_type: "#{CT_OFFICE}.drawingml.chart+xml",
|
|
147
|
+
rel_type: "#{OFFICE_REL_BASE}/chart",
|
|
148
|
+
rels_scope: :document },
|
|
149
|
+
# Images carry a per-extension Default content type resolved
|
|
150
|
+
# from the image data at save time, so content_type is nil.
|
|
151
|
+
{ key: :image, kind: :default,
|
|
152
|
+
path_pattern: "word/media/%<name>s",
|
|
153
|
+
target_pattern: "media/%<name>s",
|
|
154
|
+
rel_type: "#{OFFICE_REL_BASE}/image",
|
|
155
|
+
rels_scope: :document },
|
|
156
|
+
{ key: :ole_object, kind: :override,
|
|
157
|
+
path_pattern: "word/embeddings/%<name>s",
|
|
158
|
+
target_pattern: "embeddings/%<name>s",
|
|
159
|
+
content_type: "#{CT_OFFICE}.oleObject",
|
|
160
|
+
rel_type: "#{OFFICE_REL_BASE}/oleObject",
|
|
161
|
+
rels_scope: :document },
|
|
162
|
+
# customXml items themselves fall under the "xml" Default;
|
|
163
|
+
# only their itemProps parts get an Override.
|
|
164
|
+
{ key: :custom_xml_item, kind: :none,
|
|
165
|
+
path_pattern: "customXml/item%<index>d.xml" },
|
|
166
|
+
{ key: :custom_xml_item_props, kind: :override,
|
|
167
|
+
path_pattern: "customXml/itemProps%<index>d.xml",
|
|
168
|
+
content_type: "#{CT_OFFICE}.customXmlProperties+xml" },
|
|
169
|
+
# Hyperlink relationships target external URLs; no part, no
|
|
170
|
+
# content type.
|
|
171
|
+
{ key: :hyperlink, kind: :none,
|
|
172
|
+
rel_type: "#{OFFICE_REL_BASE}/hyperlink",
|
|
173
|
+
rels_scope: :document },
|
|
174
|
+
# Theme part of THMX (theme) packages, rooted at /theme.
|
|
175
|
+
{ key: :thmx_theme, kind: :override, path: "theme/theme1.xml",
|
|
176
|
+
content_type: "#{CT_OFFICE}.theme+xml", required: true },
|
|
177
|
+
].freeze
|
|
178
|
+
|
|
179
|
+
class << self
|
|
180
|
+
# Register a part definition. Re-registering an existing key
|
|
181
|
+
# replaces it in place (preserving position); new keys append.
|
|
182
|
+
#
|
|
183
|
+
# @param definition [PartDefinition] definition to register
|
|
184
|
+
# @return [PartDefinition] the registered definition
|
|
185
|
+
# @raise [ArgumentError] when not given a PartDefinition
|
|
186
|
+
def register(definition)
|
|
187
|
+
unless definition.is_a?(PartDefinition)
|
|
188
|
+
raise ArgumentError,
|
|
189
|
+
"expected PartDefinition, got #{definition.class}"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
index = definitions.index { |d| d.key == definition.key }
|
|
193
|
+
if index
|
|
194
|
+
definitions[index] = definition
|
|
195
|
+
else
|
|
196
|
+
definitions << definition
|
|
197
|
+
end
|
|
198
|
+
definition
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Remove a registered definition (primarily for tests).
|
|
202
|
+
#
|
|
203
|
+
# @param key [Symbol] registry key
|
|
204
|
+
# @return [Array<PartDefinition>, nil] the definitions list if
|
|
205
|
+
# a definition was removed, nil when the key was absent
|
|
206
|
+
def unregister(key)
|
|
207
|
+
definitions.reject! { |d| d.key == key.to_sym }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# @return [Array<PartDefinition>] all definitions, in
|
|
211
|
+
# registration order (a copy; safe to mutate)
|
|
212
|
+
def all
|
|
213
|
+
definitions.dup
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# @param key [Symbol] registry key (e.g. :styles)
|
|
217
|
+
# @return [PartDefinition, nil]
|
|
218
|
+
def find_by_key(key)
|
|
219
|
+
definitions.find { |d| d.key == key.to_sym }
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Find by package path; accepts both "word/styles.xml" and
|
|
223
|
+
# "/word/styles.xml" forms, and matches numbered paths against
|
|
224
|
+
# registered patterns ("word/header2.xml" → :header).
|
|
225
|
+
#
|
|
226
|
+
# @param path [String] package-relative path
|
|
227
|
+
# @return [PartDefinition, nil]
|
|
228
|
+
def find_by_path(path)
|
|
229
|
+
normalized = path.to_s.delete_prefix("/")
|
|
230
|
+
definitions.find { |d| path_match?(d, normalized) }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# @param content_type [String] MIME content type
|
|
234
|
+
# @return [PartDefinition, nil] first definition with this type
|
|
235
|
+
def find_by_content_type(content_type)
|
|
236
|
+
definitions.find { |d| d.content_type == content_type }
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Find an Override definition by content-type part name.
|
|
240
|
+
#
|
|
241
|
+
# @param part_name [String] e.g. "/word/styles.xml"
|
|
242
|
+
# @return [PartDefinition, nil]
|
|
243
|
+
def override_for(part_name)
|
|
244
|
+
normalized = part_name.to_s.delete_prefix("/")
|
|
245
|
+
definitions.find do |d|
|
|
246
|
+
d.override? && path_match?(d, normalized)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Find a Default definition by file extension.
|
|
251
|
+
#
|
|
252
|
+
# @param extension [String] e.g. "png"
|
|
253
|
+
# @return [PartDefinition, nil]
|
|
254
|
+
def default_for(extension)
|
|
255
|
+
definitions.find do |d|
|
|
256
|
+
d.default? && d.extension == extension.to_s
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Definitions included in the comprehensive [Content_Types].xml
|
|
261
|
+
# that ContentTypes.generate emits for new DOCX packages.
|
|
262
|
+
#
|
|
263
|
+
# @return [Array<PartDefinition>] in registration order
|
|
264
|
+
def standard_defaults
|
|
265
|
+
definitions.select { |d| d.standard? && d.default? }
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# @return [Array<PartDefinition>] in registration order
|
|
269
|
+
def standard_overrides
|
|
270
|
+
definitions.select { |d| d.standard? && d.override? }
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Relationship type URIs whose relationships belong in the
|
|
274
|
+
# package-level _rels/.rels part.
|
|
275
|
+
#
|
|
276
|
+
# @return [Array<String>] in registration order
|
|
277
|
+
def package_rel_types
|
|
278
|
+
definitions.filter_map { |d| d.rel_type if d.package_rel? }
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
private
|
|
282
|
+
|
|
283
|
+
def definitions
|
|
284
|
+
@definitions ||= []
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def path_match?(definition, normalized_path)
|
|
288
|
+
return true if definition.path == normalized_path
|
|
289
|
+
|
|
290
|
+
pattern = definition.path_pattern
|
|
291
|
+
!pattern.nil? && normalized_path.match?(template_regex(pattern))
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Convert a pattern like "word/header%<counter>d.xml" into a
|
|
295
|
+
# matching regexp: %<name>d → digits, %<name>s → any path run.
|
|
296
|
+
def template_regex(template)
|
|
297
|
+
source = template.split(/(%<\w+>[ds])/).map do |part|
|
|
298
|
+
case part
|
|
299
|
+
when /%<\w+>d/ then "\\d+"
|
|
300
|
+
when /%<\w+>s/ then ".+?"
|
|
301
|
+
else Regexp.escape(part)
|
|
302
|
+
end
|
|
303
|
+
end.join
|
|
304
|
+
/\A#{source}\z/
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
BUILT_INS.each { |attrs| register(PartDefinition.new(**attrs)) }
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
@@ -23,35 +23,33 @@ module Uniword
|
|
|
23
23
|
#
|
|
24
24
|
# Generate package-level .rels file
|
|
25
25
|
#
|
|
26
|
+
# Historical rId assignment: rId3 = app, rId2 = core,
|
|
27
|
+
# rId1 = document (kept for byte-identical DOTX output).
|
|
28
|
+
#
|
|
26
29
|
# @return [PackageRelationships] Relationships object for _rels/.rels
|
|
27
|
-
def self.next_available_rid(relationships)
|
|
28
|
-
max = relationships.relationships.filter_map do |r|
|
|
29
|
-
r.id[/\ArId(\d+)\z/, 1]&.to_i
|
|
30
|
-
end.max || 0
|
|
31
|
-
"rId#{max + 1}"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
30
|
def self.generate_package_rels
|
|
35
31
|
new(
|
|
36
32
|
relationships: [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
target: "docProps/app.xml",
|
|
41
|
-
),
|
|
42
|
-
PackageRelationship.new(
|
|
43
|
-
id: "rId2",
|
|
44
|
-
type: "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
|
|
45
|
-
target: "docProps/core.xml",
|
|
46
|
-
),
|
|
47
|
-
PackageRelationship.new(
|
|
48
|
-
id: "rId1",
|
|
49
|
-
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
50
|
-
target: "word/document.xml",
|
|
51
|
-
),
|
|
33
|
+
package_relationship("rId3", :app_properties),
|
|
34
|
+
package_relationship("rId2", :core_properties),
|
|
35
|
+
package_relationship("rId1", :document),
|
|
52
36
|
],
|
|
53
37
|
)
|
|
54
38
|
end
|
|
39
|
+
|
|
40
|
+
# Build one package-level relationship from the registry.
|
|
41
|
+
#
|
|
42
|
+
# @param rid [String] relationship ID
|
|
43
|
+
# @param key [Symbol] PartRegistry key
|
|
44
|
+
# @return [PackageRelationship]
|
|
45
|
+
def self.package_relationship(rid, key)
|
|
46
|
+
definition = PartRegistry.find_by_key(key)
|
|
47
|
+
PackageRelationship.new(
|
|
48
|
+
id: rid,
|
|
49
|
+
type: definition.rel_type,
|
|
50
|
+
target: definition.target,
|
|
51
|
+
)
|
|
52
|
+
end
|
|
55
53
|
end
|
|
56
54
|
end
|
|
57
55
|
end
|
|
@@ -301,12 +301,12 @@ module Uniword
|
|
|
301
301
|
def get_attribute_value(element, attr_def)
|
|
302
302
|
property_name = attr_def.property_name
|
|
303
303
|
|
|
304
|
-
#
|
|
305
|
-
#
|
|
306
|
-
#
|
|
307
|
-
#
|
|
304
|
+
# Schema-driven read: the model's declared attributes are the
|
|
305
|
+
# allowlist, and the value is fetched through the model's
|
|
306
|
+
# public method table. The property_name comes from the schema
|
|
307
|
+
# definition, not user input.
|
|
308
308
|
if element.is_a?(Lutaml::Model::Serializable) && element.class.attributes.key?(property_name)
|
|
309
|
-
element.
|
|
309
|
+
element.method(property_name).call
|
|
310
310
|
elsif element.is_a?(Hash) && element.key?(property_name)
|
|
311
311
|
element[property_name]
|
|
312
312
|
end
|
|
@@ -320,9 +320,10 @@ module Uniword
|
|
|
320
320
|
def get_child_elements(element, child_def)
|
|
321
321
|
property_name = child_def.property_name
|
|
322
322
|
|
|
323
|
-
#
|
|
323
|
+
# Schema-driven read via the model's public method table,
|
|
324
|
+
# guarded by the declared attribute names.
|
|
324
325
|
if element.is_a?(Lutaml::Model::Serializable) && element.class.attributes.key?(property_name)
|
|
325
|
-
element.
|
|
326
|
+
element.method(property_name).call
|
|
326
327
|
elsif element.is_a?(Hash) && element.key?(property_name)
|
|
327
328
|
element[property_name]
|
|
328
329
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Uniword
|
|
6
|
+
module Ooxml
|
|
7
|
+
module Types
|
|
8
|
+
# ST_HexColor (ECMA-376): RGB hex color or "auto"
|
|
9
|
+
#
|
|
10
|
+
# Constrained string type: six hexadecimal digits (case-insensitive,
|
|
11
|
+
# ST_HexColorRGB) or the literal "auto" (ST_HexColorAuto). Invalid
|
|
12
|
+
# values raise Lutaml::Model::Type::InvalidValueError at cast time
|
|
13
|
+
# (attribute assignment and XML parsing).
|
|
14
|
+
class HexColorValue < Lutaml::Model::Type::String
|
|
15
|
+
# ST_HexColor value shape: "auto" or RRGGBB hex digits
|
|
16
|
+
HEX_COLOR_PATTERN = /\A(?:auto|[0-9A-Fa-f]{6})\z/
|
|
17
|
+
|
|
18
|
+
# Cast a value to a valid ST_HexColor string
|
|
19
|
+
#
|
|
20
|
+
# @param value [Object] the raw value
|
|
21
|
+
# @param options [Hash] cast options (unused)
|
|
22
|
+
# @return [String, nil] the validated color string
|
|
23
|
+
# @raise [Lutaml::Model::Type::InvalidValueError] when the value
|
|
24
|
+
# is neither "auto" nor six hex digits
|
|
25
|
+
def self.cast(value, options = {})
|
|
26
|
+
casted = super
|
|
27
|
+
return casted if casted.nil? ||
|
|
28
|
+
Lutaml::Model::Utils.uninitialized?(casted)
|
|
29
|
+
return casted if HEX_COLOR_PATTERN.match?(casted)
|
|
30
|
+
|
|
31
|
+
raise Lutaml::Model::Type::InvalidValueError.new(
|
|
32
|
+
value, ["auto", "RRGGBB (6 hexadecimal digits)"]
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -5,29 +5,42 @@ require "lutaml/model"
|
|
|
5
5
|
module Uniword
|
|
6
6
|
module Ooxml
|
|
7
7
|
module Types
|
|
8
|
-
# OOXML Boolean type for attributes
|
|
9
|
-
# OOXML uses "1"/"0" encoding for boolean attributes
|
|
8
|
+
# OOXML Boolean type for attributes (ST_OnOff, ECMA-376)
|
|
9
|
+
# OOXML uses "1"/"0" encoding for boolean attributes; ST_OnOff also
|
|
10
|
+
# accepts the xsd:boolean spellings "true"/"false" and "on"/"off".
|
|
10
11
|
#
|
|
11
|
-
# Parsing: "1" -> true, "0" -> false
|
|
12
|
+
# Parsing: "1"/"true"/"on" -> true, "0"/"false"/"off"/nil -> false
|
|
12
13
|
# Serialization: true -> "1", false -> "0"
|
|
14
|
+
# Anything else raises Lutaml::Model::Type::InvalidValueError
|
|
15
|
+
# instead of passing through unchanged.
|
|
13
16
|
class OoxmlBoolean < Lutaml::Model::Type::Boolean
|
|
17
|
+
# Accepted ST_OnOff spellings for Boolean true
|
|
18
|
+
TRUE_VALUES = [true, 1, "1", "true", "on"].freeze
|
|
19
|
+
|
|
20
|
+
# Accepted ST_OnOff spellings for Boolean false
|
|
21
|
+
FALSE_VALUES = [false, 0, "0", "false", "off"].freeze
|
|
22
|
+
|
|
23
|
+
# All accepted ST_OnOff spellings
|
|
24
|
+
ON_OFF_VALUES = (TRUE_VALUES + FALSE_VALUES).freeze
|
|
25
|
+
|
|
14
26
|
def self.cast(value, _options = {})
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
end
|
|
27
|
+
return value if Lutaml::Model::Utils.uninitialized?(value)
|
|
28
|
+
return true if TRUE_VALUES.include?(value)
|
|
29
|
+
return false if FALSE_VALUES.include?(value) || value.nil?
|
|
30
|
+
|
|
31
|
+
raise Lutaml::Model::Type::InvalidValueError.new(
|
|
32
|
+
value, ON_OFF_VALUES
|
|
33
|
+
)
|
|
23
34
|
end
|
|
24
35
|
|
|
25
36
|
def self.serialize(value)
|
|
26
37
|
return nil if value.nil?
|
|
27
|
-
return "1" if
|
|
28
|
-
return "0" if
|
|
38
|
+
return "1" if TRUE_VALUES.include?(value)
|
|
39
|
+
return "0" if FALSE_VALUES.include?(value)
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
raise Lutaml::Model::Type::InvalidValueError.new(
|
|
42
|
+
value, ON_OFF_VALUES
|
|
43
|
+
)
|
|
31
44
|
end
|
|
32
45
|
|
|
33
46
|
# Override instance to_xml for OOXML boolean serialization
|