uniword 1.2.5 → 1.4.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 +313 -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 +22 -14
- 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 +11 -10
- 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 +12 -18
- 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 +91 -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/image_part.rb +83 -0
- data/lib/uniword/docx/package.rb +141 -333
- data/lib/uniword/docx/package_defaults.rb +83 -99
- data/lib/uniword/docx/package_integrity_checker.rb +312 -0
- data/lib/uniword/docx/package_serialization.rb +144 -233
- data/lib/uniword/docx/part.rb +125 -0
- data/lib/uniword/docx/part_collection.rb +131 -0
- data/lib/uniword/docx/part_loader/chart_loader.rb +50 -0
- data/lib/uniword/docx/part_loader/custom_xml_loader.rb +53 -0
- data/lib/uniword/docx/part_loader/embedding_loader.rb +26 -0
- data/lib/uniword/docx/part_loader/header_footer_loader.rb +101 -0
- data/lib/uniword/docx/part_loader/image_loader.rb +105 -0
- data/lib/uniword/docx/part_loader/load_context.rb +85 -0
- data/lib/uniword/docx/part_loader/raw_part_loader.rb +183 -0
- data/lib/uniword/docx/part_loader/theme_media_loader.rb +39 -0
- data/lib/uniword/docx/part_loader/xml_model_loader.rb +56 -0
- data/lib/uniword/docx/part_loader.rb +92 -0
- data/lib/uniword/docx/raw_part.rb +75 -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 +18 -31
- data/lib/uniword/docx/reconciler/notes.rb +17 -8
- data/lib/uniword/docx/reconciler/package_structure.rb +168 -221
- data/lib/uniword/docx/reconciler/parts.rb +36 -13
- data/lib/uniword/docx/reconciler/referential_integrity.rb +222 -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 +13 -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/images/image_manager.rb +13 -13
- 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/element_order.rb +94 -0
- data/lib/uniword/ooxml/part_definition.rb +329 -0
- data/lib/uniword/ooxml/part_registry.rb +504 -0
- data/lib/uniword/ooxml/relationships/image_relationship.rb +6 -3
- 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 +3 -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 +6 -5
- data/lib/uniword/transformation/mhtml_element_renderer.rb +1 -5
- data/lib/uniword/transformation/mhtml_metadata_builder.rb +6 -6
- data/lib/uniword/validation/engine.rb +35 -0
- data/lib/uniword/validation/link_checker.rb +1 -1
- data/lib/uniword/validation/opc_validator.rb +16 -2
- 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 +46 -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 +121 -122
- data/lib/uniword/wordprocessingml/document_styling.rb +246 -0
- data/lib/uniword/wordprocessingml/endnotes.rb +4 -1
- data/lib/uniword/wordprocessingml/font_replacer.rb +147 -0
- data/lib/uniword/wordprocessingml/footnotes.rb +4 -1
- 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/numbering_elements.rb +0 -15
- 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 +5 -2
- data/lib/uniword.rb +27 -2
- metadata +52 -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,329 @@
|
|
|
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, the form its
|
|
9
|
+
# [Content_Types].xml entry takes (Default by extension or Override
|
|
10
|
+
# by part name), how it is loaded from extracted ZIP content, and
|
|
11
|
+
# where it lives on both Wordprocessingml::DocumentRoot and
|
|
12
|
+
# Docx::Package. Instances are immutable value objects registered
|
|
13
|
+
# in {PartRegistry}; consumers derive every literal from them.
|
|
14
|
+
#
|
|
15
|
+
# Parts with instance-numbered paths (headers, footers, charts,
|
|
16
|
+
# customXml items) carry a +path_pattern+/+target_pattern+ with
|
|
17
|
+
# +format+-style placeholders (e.g. "word/header%<counter>d.xml")
|
|
18
|
+
# instead of a fixed path.
|
|
19
|
+
#
|
|
20
|
+
# Load behavior beyond this data (parse rules for special part
|
|
21
|
+
# kinds) lives in named Docx::PartLoader strategy classes selected
|
|
22
|
+
# by +loader+; the definition itself stays a value object.
|
|
23
|
+
#
|
|
24
|
+
# @example Fixed part
|
|
25
|
+
# PartDefinition.new(
|
|
26
|
+
# key: :styles, path: "word/styles.xml", target: "styles.xml",
|
|
27
|
+
# content_type: "...styles+xml",
|
|
28
|
+
# rel_type: ".../relationships/styles",
|
|
29
|
+
# required: true, kind: :override, rels_scope: :document,
|
|
30
|
+
# loader: :xml_model,
|
|
31
|
+
# loader_model: Uniword::Wordprocessingml::StylesConfiguration,
|
|
32
|
+
# load_priority: 50,
|
|
33
|
+
# package_attribute: :styles,
|
|
34
|
+
# document_attribute: :styles_configuration,
|
|
35
|
+
# )
|
|
36
|
+
class PartDefinition
|
|
37
|
+
# @return [Symbol] unique registry key (e.g. :styles)
|
|
38
|
+
attr_reader :key
|
|
39
|
+
|
|
40
|
+
# @return [String, nil] package-relative path ("word/styles.xml")
|
|
41
|
+
attr_reader :path
|
|
42
|
+
|
|
43
|
+
# @return [String, nil] path template for instance-numbered parts
|
|
44
|
+
attr_reader :path_pattern
|
|
45
|
+
|
|
46
|
+
# @return [String, nil] relationship Target as written in .rels
|
|
47
|
+
# (relative to the owning part; defaults to +path+)
|
|
48
|
+
attr_reader :target
|
|
49
|
+
|
|
50
|
+
# @return [String, nil] Target template for numbered parts
|
|
51
|
+
attr_reader :target_pattern
|
|
52
|
+
|
|
53
|
+
# @return [String, nil] MIME content type (nil when dynamic,
|
|
54
|
+
# e.g. images whose type depends on the file extension)
|
|
55
|
+
attr_reader :content_type
|
|
56
|
+
|
|
57
|
+
# @return [String, nil] relationship type URI (nil when the part
|
|
58
|
+
# is not referenced by a relationship)
|
|
59
|
+
attr_reader :rel_type
|
|
60
|
+
|
|
61
|
+
# @return [String, nil] file extension for Default entries
|
|
62
|
+
attr_reader :extension
|
|
63
|
+
|
|
64
|
+
# @return [Boolean] whether a minimal valid package carries it
|
|
65
|
+
attr_reader :required
|
|
66
|
+
|
|
67
|
+
# @return [Symbol] :override, :default, or :none (no
|
|
68
|
+
# [Content_Types].xml entry of its own)
|
|
69
|
+
attr_reader :kind
|
|
70
|
+
|
|
71
|
+
# @return [Symbol, nil] :package or :document — which .rels part
|
|
72
|
+
# the relationship lives in (nil when +rel_type+ is nil)
|
|
73
|
+
attr_reader :rels_scope
|
|
74
|
+
|
|
75
|
+
# @return [Boolean] whether ContentTypes.generate includes it in
|
|
76
|
+
# the comprehensive [Content_Types].xml for new DOCX packages
|
|
77
|
+
attr_reader :standard
|
|
78
|
+
|
|
79
|
+
# @return [Symbol, nil] Docx::PartLoader strategy key
|
|
80
|
+
# (:xml_model, :custom_xml, :header_footer, :chart, :image,
|
|
81
|
+
# :embedding, :theme_media); nil when the part is not loaded
|
|
82
|
+
# from ZIP content
|
|
83
|
+
attr_reader :loader
|
|
84
|
+
|
|
85
|
+
# @return [Class, nil] model class parsing the part's XML
|
|
86
|
+
# (responds to +.from_xml+); used by loaders that parse
|
|
87
|
+
attr_reader :loader_model
|
|
88
|
+
|
|
89
|
+
# @return [Symbol, nil] dynamic path resolution rule:
|
|
90
|
+
# :office_document (resolve the main document path from the
|
|
91
|
+
# package-level officeDocument relationship) or
|
|
92
|
+
# :office_document_rels (sidecar .rels of the resolved main
|
|
93
|
+
# document); nil for fixed paths
|
|
94
|
+
attr_reader :path_resolution
|
|
95
|
+
|
|
96
|
+
# @return [Integer, nil] load ordering weight — lower loads
|
|
97
|
+
# first; entries sharing a priority load in registration order
|
|
98
|
+
attr_reader :load_priority
|
|
99
|
+
|
|
100
|
+
# @return [Symbol, nil] attribute (reader/writer) holding this
|
|
101
|
+
# part on Wordprocessingml::DocumentRoot
|
|
102
|
+
attr_reader :document_attribute
|
|
103
|
+
|
|
104
|
+
# @return [Symbol, nil] attribute (reader/writer) holding this
|
|
105
|
+
# part on Docx::Package
|
|
106
|
+
attr_reader :package_attribute
|
|
107
|
+
|
|
108
|
+
# @return [Boolean] whether the package→document copy includes
|
|
109
|
+
# this part (false when the loader already places the part on
|
|
110
|
+
# the document, or the part is never read from ZIP content)
|
|
111
|
+
attr_reader :copy_to_document
|
|
112
|
+
|
|
113
|
+
# @return [Symbol, nil] predicate on the document guarding the
|
|
114
|
+
# document→package copy (e.g. :numbering_configuration_loaded?
|
|
115
|
+
# for the lazily-initialized numbering configuration)
|
|
116
|
+
attr_reader :to_package_guard
|
|
117
|
+
|
|
118
|
+
# @return [Class, nil] class the value must be an instance of
|
|
119
|
+
# for the document→package copy (e.g. CommentsPart, since
|
|
120
|
+
# DocumentRoot#comments can hold non-part values)
|
|
121
|
+
attr_reader :to_package_type
|
|
122
|
+
|
|
123
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
|
|
124
|
+
# rubocop:disable Metrics/AbcSize
|
|
125
|
+
def initialize(key:, kind:, path: nil, path_pattern: nil,
|
|
126
|
+
target: nil, target_pattern: nil,
|
|
127
|
+
content_type: nil, rel_type: nil, extension: nil,
|
|
128
|
+
required: false, rels_scope: nil, standard: false,
|
|
129
|
+
loader: nil, loader_model: nil, path_resolution: nil,
|
|
130
|
+
load_priority: nil, document_attribute: nil,
|
|
131
|
+
package_attribute: nil, copy_to_document: true,
|
|
132
|
+
to_package_guard: nil, to_package_type: nil)
|
|
133
|
+
@key = key.to_sym
|
|
134
|
+
@kind = kind
|
|
135
|
+
@path = path
|
|
136
|
+
@path_pattern = path_pattern
|
|
137
|
+
@target = target || path
|
|
138
|
+
@target_pattern = target_pattern
|
|
139
|
+
@content_type = content_type
|
|
140
|
+
@rel_type = rel_type
|
|
141
|
+
@extension = extension
|
|
142
|
+
@required = required
|
|
143
|
+
@rels_scope = rels_scope
|
|
144
|
+
@standard = standard
|
|
145
|
+
@loader = loader
|
|
146
|
+
@loader_model = loader_model
|
|
147
|
+
@path_resolution = path_resolution
|
|
148
|
+
@load_priority = load_priority
|
|
149
|
+
@document_attribute = document_attribute
|
|
150
|
+
@package_attribute = package_attribute
|
|
151
|
+
@copy_to_document = copy_to_document
|
|
152
|
+
@to_package_guard = to_package_guard
|
|
153
|
+
@to_package_type = to_package_type
|
|
154
|
+
end
|
|
155
|
+
# rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
|
|
156
|
+
# rubocop:enable Metrics/AbcSize
|
|
157
|
+
|
|
158
|
+
# @return [Boolean] true for [Content_Types].xml Override entries
|
|
159
|
+
def override?
|
|
160
|
+
kind == :override
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @return [Boolean] true for [Content_Types].xml Default entries
|
|
164
|
+
def default?
|
|
165
|
+
kind == :default
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# @return [Boolean] true when a minimal valid package carries it
|
|
169
|
+
def required?
|
|
170
|
+
!!required
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# @return [Boolean] true when ContentTypes.generate includes it
|
|
174
|
+
def standard?
|
|
175
|
+
!!standard
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @return [Boolean] true when the relationship lives in _rels/.rels
|
|
179
|
+
def package_rel?
|
|
180
|
+
rels_scope == :package
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @return [Boolean] true when the part loads from ZIP content
|
|
184
|
+
def loadable?
|
|
185
|
+
!loader.nil?
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @return [Boolean] true when the package→document copy mirrors
|
|
189
|
+
# this part onto Wordprocessingml::DocumentRoot
|
|
190
|
+
def copy_to_document?
|
|
191
|
+
!!copy_to_document
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Whether a package path matches this definition's fixed path or
|
|
195
|
+
# path pattern. Accepts both "word/styles.xml" and
|
|
196
|
+
# "/word/styles.xml" forms, and matches numbered paths against
|
|
197
|
+
# the pattern ("word/header2.xml" → :header).
|
|
198
|
+
#
|
|
199
|
+
# @param candidate [String] package-relative path
|
|
200
|
+
# @return [Boolean]
|
|
201
|
+
def match_path?(candidate)
|
|
202
|
+
normalized = candidate.to_s.delete_prefix("/")
|
|
203
|
+
return true if path == normalized
|
|
204
|
+
return false unless path_pattern
|
|
205
|
+
|
|
206
|
+
normalized.match?(template_regex(path_pattern))
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Static prefix of the path pattern up to the first placeholder
|
|
210
|
+
# (e.g. "word/theme/media/" for "word/theme/media/%<name>s").
|
|
211
|
+
#
|
|
212
|
+
# @return [String, nil] nil for fixed-path definitions
|
|
213
|
+
def pattern_prefix
|
|
214
|
+
path_pattern&.split(/%<\w+>[ds]/)&.first
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Resolve the package-relative path, interpolating pattern
|
|
218
|
+
# placeholders for instance-numbered parts.
|
|
219
|
+
#
|
|
220
|
+
# @param vars [Hash] placeholder values (e.g. counter: 1)
|
|
221
|
+
# @return [String, nil] e.g. "word/header1.xml"
|
|
222
|
+
def path_for(**vars)
|
|
223
|
+
expand(path_pattern || path, vars)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Resolve the content-type Override part name (leading slash).
|
|
227
|
+
#
|
|
228
|
+
# @param vars [Hash] placeholder values for numbered parts
|
|
229
|
+
# @return [String, nil] e.g. "/word/header1.xml"
|
|
230
|
+
def part_name_for(**vars)
|
|
231
|
+
resolved = path_for(**vars)
|
|
232
|
+
resolved && "/#{resolved}"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Resolve the relationship Target attribute.
|
|
236
|
+
#
|
|
237
|
+
# @param vars [Hash] placeholder values for numbered parts
|
|
238
|
+
# @return [String, nil] e.g. "header1.xml"
|
|
239
|
+
def target_for(**vars)
|
|
240
|
+
expand(target_pattern || target, vars)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Fixed-path Override part name (leading slash).
|
|
244
|
+
#
|
|
245
|
+
# @return [String, nil] e.g. "/word/styles.xml"
|
|
246
|
+
def part_name
|
|
247
|
+
path && "/#{path}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Package paths this part kind emits for the given package.
|
|
251
|
+
#
|
|
252
|
+
# Single-instance kinds emit their fixed +path+ when the backing
|
|
253
|
+
# model is present; collection families emit each stored part's
|
|
254
|
+
# own paths (each part object answers +package_paths+).
|
|
255
|
+
#
|
|
256
|
+
# @param package [Docx::Package] Package to enumerate for
|
|
257
|
+
# @return [Array<String>] emitted package paths (possibly empty)
|
|
258
|
+
def emitted_paths(package)
|
|
259
|
+
source = attribute_source(package)
|
|
260
|
+
return [] if source.nil?
|
|
261
|
+
|
|
262
|
+
collection? ? collection_paths(source) : [path]
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Two definitions are equal when every field matches.
|
|
266
|
+
def ==(other)
|
|
267
|
+
other.is_a?(PartDefinition) && fields == other.fields
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
protected
|
|
271
|
+
|
|
272
|
+
# @return [Array] field values in declaration order, used for
|
|
273
|
+
# value equality
|
|
274
|
+
# rubocop:disable Metrics/AbcSize
|
|
275
|
+
def fields
|
|
276
|
+
[key, kind, path, path_pattern, target, target_pattern,
|
|
277
|
+
content_type, rel_type, extension, required, rels_scope,
|
|
278
|
+
standard, loader, loader_model, path_resolution,
|
|
279
|
+
load_priority, document_attribute, package_attribute,
|
|
280
|
+
copy_to_document, to_package_guard, to_package_type]
|
|
281
|
+
end
|
|
282
|
+
# rubocop:enable Metrics/AbcSize
|
|
283
|
+
|
|
284
|
+
private
|
|
285
|
+
|
|
286
|
+
# The model object backing this part kind on the package: the
|
|
287
|
+
# package attribute when set, else the document attribute read
|
|
288
|
+
# through the package's document.
|
|
289
|
+
def attribute_source(package)
|
|
290
|
+
value = package_attribute && package.method(package_attribute).call
|
|
291
|
+
return value if value
|
|
292
|
+
|
|
293
|
+
document_attribute &&
|
|
294
|
+
package.document&.method(document_attribute)&.call
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Families (path_pattern, no fixed path) enumerate their parts;
|
|
298
|
+
# single-instance kinds do not.
|
|
299
|
+
def collection?
|
|
300
|
+
path.nil?
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def collection_paths(source)
|
|
304
|
+
values = source.is_a?(Docx::PartCollection) ? source.values : source
|
|
305
|
+
values.flat_map(&:package_paths)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def expand(template, vars)
|
|
309
|
+
return nil unless template
|
|
310
|
+
return template if vars.empty?
|
|
311
|
+
|
|
312
|
+
format(template, vars)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Convert a pattern like "word/header%<counter>d.xml" into a
|
|
316
|
+
# matching regexp: %<name>d → digits, %<name>s → any path run.
|
|
317
|
+
def template_regex(template)
|
|
318
|
+
source = template.split(/(%<\w+>[ds])/).map do |part|
|
|
319
|
+
case part
|
|
320
|
+
when /%<\w+>d/ then "\\d+"
|
|
321
|
+
when /%<\w+>s/ then ".+?"
|
|
322
|
+
else Regexp.escape(part)
|
|
323
|
+
end
|
|
324
|
+
end.join
|
|
325
|
+
/\A#{source}\z/
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|