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.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +313 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/config/ooxml/schemas/shared_types.yml +5 -4
  5. data/data/schemas/ecma/dc.xsd +118 -0
  6. data/data/schemas/ecma/dcmitype.xsd +50 -0
  7. data/data/schemas/ecma/dcterms.xsd +331 -0
  8. data/data/schemas/ecma/opc-coreProperties.xsd +2 -2
  9. data/data/schemas/ecma/xml.xsd +117 -0
  10. data/lib/uniword/builder/bibliography_builder.rb +1 -1
  11. data/lib/uniword/builder/chart_builder.rb +22 -14
  12. data/lib/uniword/builder/comment_anchorer.rb +221 -0
  13. data/lib/uniword/builder/document_builder.rb +97 -6
  14. data/lib/uniword/builder/image_builder.rb +11 -10
  15. data/lib/uniword/builder/run_utils.rb +2 -0
  16. data/lib/uniword/builder.rb +1 -0
  17. data/lib/uniword/cli/fonts_cli.rb +52 -0
  18. data/lib/uniword/cli/main.rb +110 -11
  19. data/lib/uniword/cli/page_cli.rb +85 -0
  20. data/lib/uniword/cli/styles_cli.rb +204 -0
  21. data/lib/uniword/cli/theme_cli.rb +88 -0
  22. data/lib/uniword/cli/toc_cli.rb +22 -2
  23. data/lib/uniword/comment.rb +20 -15
  24. data/lib/uniword/comment_range.rb +3 -3
  25. data/lib/uniword/comments_part.rb +51 -19
  26. data/lib/uniword/configuration/configuration_loader.rb +5 -3
  27. data/lib/uniword/configuration.rb +93 -1
  28. data/lib/uniword/content_types.rb +19 -39
  29. data/lib/uniword/document_factory.rb +12 -18
  30. data/lib/uniword/document_writer.rb +16 -5
  31. data/lib/uniword/docx/chart_part.rb +34 -0
  32. data/lib/uniword/docx/custom_xml_item.rb +91 -0
  33. data/lib/uniword/docx/header_footer_part.rb +140 -0
  34. data/lib/uniword/docx/header_footer_part_collection.rb +135 -0
  35. data/lib/uniword/docx/header_footer_view.rb +225 -0
  36. data/lib/uniword/docx/id_allocator.rb +130 -36
  37. data/lib/uniword/docx/image_part.rb +83 -0
  38. data/lib/uniword/docx/package.rb +141 -333
  39. data/lib/uniword/docx/package_defaults.rb +83 -99
  40. data/lib/uniword/docx/package_integrity_checker.rb +312 -0
  41. data/lib/uniword/docx/package_serialization.rb +144 -233
  42. data/lib/uniword/docx/part.rb +125 -0
  43. data/lib/uniword/docx/part_collection.rb +131 -0
  44. data/lib/uniword/docx/part_loader/chart_loader.rb +50 -0
  45. data/lib/uniword/docx/part_loader/custom_xml_loader.rb +53 -0
  46. data/lib/uniword/docx/part_loader/embedding_loader.rb +26 -0
  47. data/lib/uniword/docx/part_loader/header_footer_loader.rb +101 -0
  48. data/lib/uniword/docx/part_loader/image_loader.rb +105 -0
  49. data/lib/uniword/docx/part_loader/load_context.rb +85 -0
  50. data/lib/uniword/docx/part_loader/raw_part_loader.rb +183 -0
  51. data/lib/uniword/docx/part_loader/theme_media_loader.rb +39 -0
  52. data/lib/uniword/docx/part_loader/xml_model_loader.rb +56 -0
  53. data/lib/uniword/docx/part_loader.rb +92 -0
  54. data/lib/uniword/docx/raw_part.rb +75 -0
  55. data/lib/uniword/docx/reconciler/body.rb +59 -68
  56. data/lib/uniword/docx/reconciler/fix.rb +46 -0
  57. data/lib/uniword/docx/reconciler/fix_codes.rb +37 -30
  58. data/lib/uniword/docx/reconciler/helpers.rb +18 -31
  59. data/lib/uniword/docx/reconciler/notes.rb +17 -8
  60. data/lib/uniword/docx/reconciler/package_structure.rb +168 -221
  61. data/lib/uniword/docx/reconciler/parts.rb +36 -13
  62. data/lib/uniword/docx/reconciler/referential_integrity.rb +222 -123
  63. data/lib/uniword/docx/reconciler/tables.rb +78 -16
  64. data/lib/uniword/docx/reconciler/theme.rb +7 -4
  65. data/lib/uniword/docx/reconciler.rb +36 -14
  66. data/lib/uniword/docx.rb +13 -0
  67. data/lib/uniword/drawingml/blip.rb +0 -1
  68. data/lib/uniword/drawingml/color_scheme.rb +5 -6
  69. data/lib/uniword/drawingml/font_scheme.rb +4 -8
  70. data/lib/uniword/drawingml/theme.rb +12 -6
  71. data/lib/uniword/errors.rb +8 -1
  72. data/lib/uniword/footer.rb +4 -0
  73. data/lib/uniword/header.rb +4 -0
  74. data/lib/uniword/hyperlink.rb +1 -1
  75. data/lib/uniword/image.rb +6 -6
  76. data/lib/uniword/images/image_manager.rb +13 -13
  77. data/lib/uniword/lazy_loader.rb +1 -2
  78. data/lib/uniword/model_attribute_access.rb +20 -4
  79. data/lib/uniword/ooxml/dotx_package.rb +42 -2
  80. data/lib/uniword/ooxml/element_order.rb +94 -0
  81. data/lib/uniword/ooxml/part_definition.rb +329 -0
  82. data/lib/uniword/ooxml/part_registry.rb +504 -0
  83. data/lib/uniword/ooxml/relationships/image_relationship.rb +6 -3
  84. data/lib/uniword/ooxml/relationships/package_relationships.rb +20 -22
  85. data/lib/uniword/ooxml/schema/element_serializer.rb +8 -7
  86. data/lib/uniword/ooxml/types/hex_color_value.rb +38 -0
  87. data/lib/uniword/ooxml/types/ooxml_boolean.rb +27 -14
  88. data/lib/uniword/ooxml/types/ooxml_boolean_optional.rb +21 -16
  89. data/lib/uniword/ooxml/types/theme_color_value.rb +22 -0
  90. data/lib/uniword/ooxml/types/unsigned_decimal_number.rb +30 -0
  91. data/lib/uniword/ooxml/types.rb +10 -0
  92. data/lib/uniword/ooxml.rb +3 -0
  93. data/lib/uniword/properties/alignment.rb +8 -3
  94. data/lib/uniword/properties/border.rb +52 -6
  95. data/lib/uniword/properties/cell_vertical_align.rb +4 -1
  96. data/lib/uniword/properties/color_value.rb +8 -3
  97. data/lib/uniword/properties/highlight.rb +9 -5
  98. data/lib/uniword/properties/shading.rb +4 -3
  99. data/lib/uniword/properties/tab_stop.rb +6 -1
  100. data/lib/uniword/properties/table_justification.rb +4 -1
  101. data/lib/uniword/properties/underline.rb +4 -2
  102. data/lib/uniword/properties/vertical_align.rb +6 -3
  103. data/lib/uniword/review/review_manager.rb +23 -9
  104. data/lib/uniword/revision.rb +9 -9
  105. data/lib/uniword/schema/model_generator.rb +31 -15
  106. data/lib/uniword/shared_types/hex_color.rb +4 -1
  107. data/lib/uniword/shared_types/pixel_measure.rb +4 -1
  108. data/lib/uniword/shared_types/point_measure.rb +4 -1
  109. data/lib/uniword/shared_types/text_alignment.rb +6 -1
  110. data/lib/uniword/shared_types/twips_measure.rb +4 -1
  111. data/lib/uniword/spreadsheetml/phonetic_pr.rb +1 -1
  112. data/lib/uniword/spreadsheetml/shared_string_table.rb +2 -2
  113. data/lib/uniword/spreadsheetml/table_formula.rb +1 -1
  114. data/lib/uniword/template/variable_resolver.rb +40 -7
  115. data/lib/uniword/themes/theme_transformation.rb +55 -41
  116. data/lib/uniword/toc/toc_generator.rb +6 -5
  117. data/lib/uniword/transformation/mhtml_element_renderer.rb +1 -5
  118. data/lib/uniword/transformation/mhtml_metadata_builder.rb +6 -6
  119. data/lib/uniword/validation/engine.rb +35 -0
  120. data/lib/uniword/validation/link_checker.rb +1 -1
  121. data/lib/uniword/validation/opc_validator.rb +16 -2
  122. data/lib/uniword/validation/report/layer_result.rb +0 -1
  123. data/lib/uniword/validation/report/terminal_formatter.rb +0 -1
  124. data/lib/uniword/validation/report/verification_report.rb +0 -2
  125. data/lib/uniword/validation/report.rb +20 -0
  126. data/lib/uniword/validation/rules/base.rb +10 -2
  127. data/lib/uniword/validation/rules/bookmark_pairing_rule.rb +38 -0
  128. data/lib/uniword/validation/rules/bookmark_uniqueness_rule.rb +48 -0
  129. data/lib/uniword/validation/rules/bookmarks_rule.rb +0 -2
  130. data/lib/uniword/validation/rules/content_types_coverage_rule.rb +0 -2
  131. data/lib/uniword/validation/rules/core_properties_namespace_rule.rb +0 -2
  132. data/lib/uniword/validation/rules/document_body_rule.rb +25 -0
  133. data/lib/uniword/validation/rules/document_context.rb +7 -0
  134. data/lib/uniword/validation/rules/empty_paragraphs_rule.rb +27 -0
  135. data/lib/uniword/validation/rules/font_table_signature_rule.rb +0 -2
  136. data/lib/uniword/validation/rules/fonts_rule.rb +0 -2
  137. data/lib/uniword/validation/rules/footnotes_rule.rb +0 -2
  138. data/lib/uniword/validation/rules/headers_footers_rule.rb +0 -2
  139. data/lib/uniword/validation/rules/images_rule.rb +0 -2
  140. data/lib/uniword/validation/rules/mc_ignorable_namespace_rule.rb +0 -2
  141. data/lib/uniword/validation/rules/model_context.rb +32 -0
  142. data/lib/uniword/validation/rules/model_rule.rb +34 -0
  143. data/lib/uniword/validation/rules/numbering_preservation_rule.rb +0 -2
  144. data/lib/uniword/validation/rules/numbering_rule.rb +0 -2
  145. data/lib/uniword/validation/rules/relationship_integrity_rule.rb +0 -1
  146. data/lib/uniword/validation/rules/rsid_rule.rb +0 -2
  147. data/lib/uniword/validation/rules/section_properties_rule.rb +0 -2
  148. data/lib/uniword/validation/rules/settings_rule.rb +0 -2
  149. data/lib/uniword/validation/rules/settings_values_rule.rb +0 -2
  150. data/lib/uniword/validation/rules/style_references_rule.rb +0 -2
  151. data/lib/uniword/validation/rules/table_grid_rule.rb +27 -0
  152. data/lib/uniword/validation/rules/table_properties_rule.rb +27 -0
  153. data/lib/uniword/validation/rules/tables_rule.rb +0 -2
  154. data/lib/uniword/validation/rules/theme_completeness_rule.rb +0 -2
  155. data/lib/uniword/validation/rules/theme_rule.rb +0 -2
  156. data/lib/uniword/validation/rules.rb +61 -23
  157. data/lib/uniword/validation/schema_registry.rb +11 -0
  158. data/lib/uniword/validation/validators/document_semantics_validator.rb +1 -9
  159. data/lib/uniword/validation/validators/xml_schema_validator.rb +46 -2
  160. data/lib/uniword/validation/validators.rb +0 -9
  161. data/lib/uniword/validation/verify_orchestrator.rb +0 -7
  162. data/lib/uniword/validation.rb +7 -2
  163. data/lib/uniword/version.rb +1 -1
  164. data/lib/uniword/vml/imagedata.rb +0 -1
  165. data/lib/uniword/wordprocessingml/attached_template.rb +0 -1
  166. data/lib/uniword/wordprocessingml/deleted_text.rb +1 -1
  167. data/lib/uniword/wordprocessingml/document_root.rb +121 -122
  168. data/lib/uniword/wordprocessingml/document_styling.rb +246 -0
  169. data/lib/uniword/wordprocessingml/endnotes.rb +4 -1
  170. data/lib/uniword/wordprocessingml/font_replacer.rb +147 -0
  171. data/lib/uniword/wordprocessingml/footnotes.rb +4 -1
  172. data/lib/uniword/wordprocessingml/hdr_shape_defaults.rb +0 -1
  173. data/lib/uniword/wordprocessingml/hyperlink.rb +0 -1
  174. data/lib/uniword/wordprocessingml/level.rb +6 -20
  175. data/lib/uniword/wordprocessingml/math_pr.rb +0 -11
  176. data/lib/uniword/wordprocessingml/numbering_definition.rb +16 -8
  177. data/lib/uniword/wordprocessingml/numbering_elements.rb +0 -15
  178. data/lib/uniword/wordprocessingml/page_setup.rb +181 -0
  179. data/lib/uniword/wordprocessingml/paragraph_properties.rb +33 -28
  180. data/lib/uniword/wordprocessingml/rsids.rb +0 -2
  181. data/lib/uniword/wordprocessingml/run.rb +3 -0
  182. data/lib/uniword/wordprocessingml/section_properties.rb +2 -2
  183. data/lib/uniword/wordprocessingml/settings.rb +8 -34
  184. data/lib/uniword/wordprocessingml/shape_defaults.rb +0 -1
  185. data/lib/uniword/wordprocessingml/style.rb +2 -1
  186. data/lib/uniword/wordprocessingml/style_cleanup.rb +198 -0
  187. data/lib/uniword/wordprocessingml/table_cell_properties.rb +6 -6
  188. data/lib/uniword/wordprocessingml/update_fields.rb +27 -0
  189. data/lib/uniword/wordprocessingml/w14_attributes.rb +30 -2
  190. data/lib/uniword/wordprocessingml.rb +5 -2
  191. data/lib/uniword.rb +27 -2
  192. metadata +52 -19
  193. data/config/validation_rules.yml +0 -60
  194. data/config/warning_rules.yml +0 -57
  195. data/lib/uniword/validation/document_validator.rb +0 -272
  196. data/lib/uniword/validation/structural_validator.rb +0 -116
  197. data/lib/uniword/validation/validators/content_type_validator.rb +0 -165
  198. data/lib/uniword/validation/validators/file_structure_validator.rb +0 -104
  199. data/lib/uniword/validation/validators/ooxml_part_validator.rb +0 -128
  200. data/lib/uniword/validation/validators/relationship_validator.rb +0 -147
  201. data/lib/uniword/validation/validators/zip_integrity_validator.rb +0 -110
  202. data/lib/uniword/validators/element_validator.rb +0 -93
  203. data/lib/uniword/validators/paragraph_validator.rb +0 -116
  204. data/lib/uniword/validators/table_validator.rb +0 -134
  205. data/lib/uniword/validators.rb +0 -9
  206. data/lib/uniword/warnings/warning.rb +0 -130
  207. data/lib/uniword/warnings/warning_collector.rb +0 -234
  208. data/lib/uniword/warnings/warning_report.rb +0 -159
  209. data/lib/uniword/warnings.rb +0 -9
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Validation
5
+ module Rules
6
+ # In-memory validation context: wraps a document model.
7
+ #
8
+ # Counterpart to DocumentContext (which wraps an on-disk DOCX
9
+ # package). Model-level rules consume this context via the Engine.
10
+ #
11
+ # @example Validate a built document
12
+ # context = ModelContext.new(document)
13
+ # issues = Validation::Engine.run(context)
14
+ class ModelContext
15
+ # @return [Wordprocessingml::DocumentRoot] the document model
16
+ attr_reader :document
17
+
18
+ # @param document [Wordprocessingml::DocumentRoot] in-memory model
19
+ def initialize(document)
20
+ @document = document
21
+ end
22
+
23
+ # Context type used by the Engine to select rules.
24
+ #
25
+ # @return [Symbol] :model — this context wraps an in-memory model
26
+ def context_type
27
+ :model
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Validation
5
+ module Rules
6
+ # Base class for in-memory (model-level) validation rules.
7
+ #
8
+ # Model rules run against a Rules::ModelContext — the pre-save
9
+ # front-end of the validation engine — and inspect the document
10
+ # model directly instead of parsing package XML.
11
+ #
12
+ # @example Implement a model rule
13
+ # class MyModelRule < ModelRule
14
+ # def code = "DOC-900"
15
+ #
16
+ # def check(context)
17
+ # return [] if context.document.body
18
+ #
19
+ # [issue("Document body is missing")]
20
+ # end
21
+ # end
22
+ #
23
+ # Rules::Registry.register(MyModelRule)
24
+ class ModelRule < Base
25
+ # Model rules consume the in-memory ModelContext.
26
+ #
27
+ # @return [Symbol] :model
28
+ def context_type
29
+ :model
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "zip"
4
- require_relative "base"
5
4
 
6
5
  module Uniword
7
6
  module Validation
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Validation
5
+ module Rules
6
+ # DOC-204: every table must have a tblGrid.
7
+ #
8
+ # wml.xsd CT_Tbl requires exactly one tblGrid child; without it
9
+ # Word cannot lay out column widths and repairs the document.
10
+ class TableGridRule < ModelRule
11
+ def code = "DOC-204"
12
+ def category = :tables
13
+ def severity = "error"
14
+
15
+ def check(context)
16
+ context.document.tables.each_with_index.filter_map do |table, idx|
17
+ next if table.grid
18
+
19
+ issue("Table #{idx + 1} is missing the required tblGrid element",
20
+ part: "word/document.xml",
21
+ suggestion: "Add a w:tblGrid with one w:gridCol per column.")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Validation
5
+ module Rules
6
+ # DOC-205: every table must have table properties (tblPr).
7
+ #
8
+ # wml.xsd CT_Tbl requires exactly one tblPr child as the table's
9
+ # first property element.
10
+ class TablePropertiesRule < ModelRule
11
+ def code = "DOC-205"
12
+ def category = :tables
13
+ def severity = "error"
14
+
15
+ def check(context)
16
+ context.document.tables.each_with_index.filter_map do |table, idx|
17
+ next if table.properties
18
+
19
+ issue("Table #{idx + 1} is missing the required tblPr element",
20
+ part: "word/document.xml",
21
+ suggestion: "Add a w:tblPr element to the table.")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
5
3
  module Uniword
6
4
  module Validation
7
5
  module Rules
@@ -1,30 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rules/base"
4
- require_relative "rules/registry"
5
- require_relative "rules/document_context"
6
- require_relative "rules/style_references_rule"
7
- require_relative "rules/numbering_rule"
8
- require_relative "rules/footnotes_rule"
9
- require_relative "rules/headers_footers_rule"
10
- require_relative "rules/bookmarks_rule"
11
- require_relative "rules/images_rule"
12
- require_relative "rules/tables_rule"
13
- require_relative "rules/fonts_rule"
14
- require_relative "rules/theme_rule"
15
- require_relative "rules/settings_rule"
16
- require_relative "rules/mc_ignorable_namespace_rule"
17
- require_relative "rules/settings_values_rule"
18
- require_relative "rules/theme_completeness_rule"
19
- require_relative "rules/numbering_preservation_rule"
20
- require_relative "rules/section_properties_rule"
21
- require_relative "rules/core_properties_namespace_rule"
22
- require_relative "rules/content_types_coverage_rule"
23
- require_relative "rules/font_table_signature_rule"
24
- require_relative "rules/relationship_integrity_rule"
25
- require_relative "rules/rsid_rule"
3
+ module Uniword
4
+ module Validation
5
+ # Document semantic validation rules and their registry.
6
+ module Rules
7
+ autoload :Base, "#{__dir__}/rules/base"
8
+ autoload :Registry, "#{__dir__}/rules/registry"
9
+ autoload :DocumentContext, "#{__dir__}/rules/document_context"
10
+ autoload :ModelContext, "#{__dir__}/rules/model_context"
11
+ autoload :ModelRule, "#{__dir__}/rules/model_rule"
12
+ autoload :DocumentBodyRule, "#{__dir__}/rules/document_body_rule"
13
+ autoload :BookmarkPairingRule,
14
+ "#{__dir__}/rules/bookmark_pairing_rule"
15
+ autoload :BookmarkUniquenessRule,
16
+ "#{__dir__}/rules/bookmark_uniqueness_rule"
17
+ autoload :EmptyParagraphsRule,
18
+ "#{__dir__}/rules/empty_paragraphs_rule"
19
+ autoload :TableGridRule, "#{__dir__}/rules/table_grid_rule"
20
+ autoload :TablePropertiesRule,
21
+ "#{__dir__}/rules/table_properties_rule"
22
+ autoload :StyleReferencesRule,
23
+ "#{__dir__}/rules/style_references_rule"
24
+ autoload :NumberingRule, "#{__dir__}/rules/numbering_rule"
25
+ autoload :FootnotesRule, "#{__dir__}/rules/footnotes_rule"
26
+ autoload :HeadersFootersRule,
27
+ "#{__dir__}/rules/headers_footers_rule"
28
+ autoload :BookmarksRule, "#{__dir__}/rules/bookmarks_rule"
29
+ autoload :ImagesRule, "#{__dir__}/rules/images_rule"
30
+ autoload :TablesRule, "#{__dir__}/rules/tables_rule"
31
+ autoload :FontsRule, "#{__dir__}/rules/fonts_rule"
32
+ autoload :ThemeRule, "#{__dir__}/rules/theme_rule"
33
+ autoload :SettingsRule, "#{__dir__}/rules/settings_rule"
34
+ autoload :McIgnorableNamespaceRule,
35
+ "#{__dir__}/rules/mc_ignorable_namespace_rule"
36
+ autoload :SettingsValuesRule,
37
+ "#{__dir__}/rules/settings_values_rule"
38
+ autoload :ThemeCompletenessRule,
39
+ "#{__dir__}/rules/theme_completeness_rule"
40
+ autoload :NumberingPreservationRule,
41
+ "#{__dir__}/rules/numbering_preservation_rule"
42
+ autoload :SectionPropertiesRule,
43
+ "#{__dir__}/rules/section_properties_rule"
44
+ autoload :CorePropertiesNamespaceRule,
45
+ "#{__dir__}/rules/core_properties_namespace_rule"
46
+ autoload :ContentTypesCoverageRule,
47
+ "#{__dir__}/rules/content_types_coverage_rule"
48
+ autoload :FontTableSignatureRule,
49
+ "#{__dir__}/rules/font_table_signature_rule"
50
+ autoload :RelationshipIntegrityRule,
51
+ "#{__dir__}/rules/relationship_integrity_rule"
52
+ autoload :RsidRule, "#{__dir__}/rules/rsid_rule"
53
+ end
54
+ end
55
+ end
26
56
 
27
57
  # Register all built-in validation rules
58
+ # Model-level (in-memory) rules
59
+ Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::DocumentBodyRule)
60
+ Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::BookmarkPairingRule)
61
+ Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::BookmarkUniquenessRule)
62
+ Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::EmptyParagraphsRule)
63
+ Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::TableGridRule)
64
+ Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::TablePropertiesRule)
65
+ # Package-level (on-disk) rules
28
66
  Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::StyleReferencesRule)
29
67
  Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::NumberingRule)
30
68
  Uniword::Validation::Rules::Registry.register(Uniword::Validation::Rules::FootnotesRule)
@@ -57,6 +57,7 @@ module Uniword
57
57
  word/document.xml
58
58
  word/styles.xml
59
59
  word/settings.xml
60
+ word/webSettings.xml
60
61
  word/fontTable.xml
61
62
  word/numbering.xml
62
63
  word/footnotes.xml
@@ -64,6 +65,13 @@ module Uniword
64
65
  word/comments.xml
65
66
  ].freeze
66
67
 
68
+ # Document property parts and their primary schemas.
69
+ DOCPROPS_SCHEMAS = {
70
+ "docProps/core.xml" => "ecma/opc-coreProperties.xsd",
71
+ "docProps/app.xml" => "iso/shared-documentPropertiesExtended.xsd",
72
+ "docProps/custom.xml" => "iso/shared-documentPropertiesCustom.xsd",
73
+ }.freeze
74
+
67
75
  # Pattern for header/footer parts
68
76
  HEADER_FOOTER_PATTERN = %r{\Aword/(header|footer)\d*\.xml\z}
69
77
 
@@ -115,6 +123,7 @@ module Uniword
115
123
  # For Word parts (document.xml, styles.xml, etc.), returns wml-2010.xsd
116
124
  # which imports the base wml.xsd and all extension schemas.
117
125
  # For relationship and content type parts, returns the appropriate schema.
126
+ # For docProps parts, returns the OPC/extended/custom properties schema.
118
127
  #
119
128
  # @param part_name [String] Path within ZIP (e.g., "word/document.xml")
120
129
  # @return [String, nil] XSD path relative to schemas_dir
@@ -124,6 +133,8 @@ module Uniword
124
133
  "ecma/opc-contentTypes.xsd"
125
134
  when "_rels/.rels", ->(n) { n.match?(RELS_PATTERN) }
126
135
  "ecma/opc-relationships.xsd"
136
+ when *DOCPROPS_SCHEMAS.keys
137
+ DOCPROPS_SCHEMAS[part_name]
127
138
  when *WORDML_PARTS, ->(n) { n.match?(HEADER_FOOTER_PATTERN) }
128
139
  "microsoft/wml-2010.xsd"
129
140
  when ->(n) { n.match?(THEME_PATTERN) }
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "zip"
4
4
  require "nokogiri"
5
- require_relative "../rules"
6
- require_relative "../report/validation_issue"
7
5
  # LayerValidator autoloaded via lib/uniword/validation.rb
8
6
 
9
7
  module Uniword
@@ -29,15 +27,9 @@ module Uniword
29
27
 
30
28
  def validate(path)
31
29
  issues = []
32
-
33
30
  context = Rules::DocumentContext.new(path)
34
31
  begin
35
- rules = Rules::Registry.all
36
- rules.each do |rule|
37
- next unless rule.applicable?(context)
38
-
39
- issues.concat(rule.check(context))
40
- end
32
+ issues = Engine.run(context)
41
33
  ensure
42
34
  context.close
43
35
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "zip"
4
4
  require "nokogiri"
5
- require_relative "../schema_registry"
6
- require_relative "../report/validation_issue"
7
5
  # LayerValidator autoloaded via lib/uniword/validation.rb
8
6
 
9
7
  module Uniword
@@ -22,6 +20,11 @@ module Uniword
22
20
  # validator = XmlSchemaValidator.new("xml_schema" => { "xsd_validation" => true })
23
21
  # result = validator.validate('/path/to/document.docx')
24
22
  class XmlSchemaValidator < LayerValidator
23
+ # Markup Compatibility namespace — the mc:Ignorable attribute
24
+ # itself lives here and is stripped during MCE preprocessing.
25
+ MCE_NAMESPACE_URI =
26
+ "http://schemas.openxmlformats.org/markup-compatibility/2006"
27
+
25
28
  def layer_name
26
29
  "XSD Schema"
27
30
  end
@@ -85,6 +88,7 @@ module Uniword
85
88
  content = entry.get_input_stream.read
86
89
  schema = registry.load_schema(schema_path)
87
90
  doc = Nokogiri::XML(content)
91
+ preprocess_mce(doc)
88
92
 
89
93
  schema.validate(doc).each do |error|
90
94
  issues << Report::ValidationIssue.new(
@@ -116,6 +120,46 @@ module Uniword
116
120
  end
117
121
  end
118
122
 
123
+ # Strip MCE-ignorable ATTRIBUTES per the part's own mc:Ignorable
124
+ # declaration (w14:paraId/textId and the mc:Ignorable attribute
125
+ # itself), so XSD validation is not drowned in false errors for
126
+ # content every real document carries.
127
+ #
128
+ # Extension ELEMENTS are deliberately kept: removing them changes
129
+ # libxml2's content-model evaluation and can expose pre-existing
130
+ # Word-vs-schema quirks (e.g. rsids placed late, tolerated via the
131
+ # sequence's trailing xsd:any) as false baseline errors.
132
+ # Operates in place on the parsed document; never touches the
133
+ # packaged bytes.
134
+ #
135
+ # @param doc [Nokogiri::XML::Document] parsed part to clean
136
+ # @return [void]
137
+ def preprocess_mce(doc)
138
+ root = doc.root
139
+ return unless root
140
+
141
+ ignorable = root["Ignorable"] || root["mc:Ignorable"]
142
+ return if ignorable.nil? || ignorable.empty?
143
+
144
+ uris = ignorable.split(/\s+/).filter_map do |prefix|
145
+ root.namespaces["xmlns:#{prefix}"]
146
+ end
147
+ # The mc:Ignorable attribute itself is MCE machinery and is
148
+ # undeclared on most root elements in the transitional XSDs.
149
+ strip_mce_attributes(root, uris + [MCE_NAMESPACE_URI])
150
+ end
151
+
152
+ # Remove attributes in ignorable namespaces from every element.
153
+ def strip_mce_attributes(doc, uris)
154
+ doc.traverse do |node|
155
+ next unless node.element?
156
+
157
+ node.attribute_nodes.each do |attr|
158
+ attr.remove if uris.include?(attr.namespace&.href)
159
+ end
160
+ end
161
+ end
162
+
119
163
  def check_unknown_namespaces(registry, content, part_name, issues)
120
164
  ns_uris = registry.detect_namespaces(content)
121
165
  unknown = registry.unknown_namespaces(ns_uris)
@@ -3,16 +3,7 @@
3
3
  module Uniword
4
4
  module Validation
5
5
  module Validators
6
- autoload :FileStructureValidator,
7
- "#{__dir__}/validators/file_structure_validator"
8
- autoload :ZipIntegrityValidator,
9
- "#{__dir__}/validators/zip_integrity_validator"
10
- autoload :OoxmlPartValidator, "#{__dir__}/validators/ooxml_part_validator"
11
6
  autoload :XmlSchemaValidator, "#{__dir__}/validators/xml_schema_validator"
12
- autoload :RelationshipValidator,
13
- "#{__dir__}/validators/relationship_validator"
14
- autoload :ContentTypeValidator,
15
- "#{__dir__}/validators/content_type_validator"
16
7
  autoload :DocumentSemanticsValidator,
17
8
  "#{__dir__}/validators/document_semantics_validator"
18
9
  end
@@ -1,13 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "time"
4
- require_relative "opc_validator"
5
- require_relative "schema_registry"
6
- require_relative "report/verification_report"
7
- require_relative "report/layer_result"
8
- require_relative "report/terminal_formatter"
9
- require_relative "validators/xml_schema_validator"
10
- require_relative "validators/document_semantics_validator"
11
4
 
12
5
  module Uniword
13
6
  module Validation
@@ -9,9 +9,14 @@ module Uniword
9
9
  autoload :LayerValidator, "#{__dir__}/validation/layer_validator"
10
10
  autoload :LayerValidationResult,
11
11
  "#{__dir__}/validation/layer_validation_result"
12
- autoload :DocumentValidator, "#{__dir__}/validation/document_validator"
13
12
  autoload :Checkers, "#{__dir__}/validation/checkers"
14
13
  autoload :Validators, "#{__dir__}/validation/validators"
15
- autoload :StructuralValidator, "#{__dir__}/validation/structural_validator"
14
+ autoload :Engine, "#{__dir__}/validation/engine"
15
+ autoload :OpcValidator, "#{__dir__}/validation/opc_validator"
16
+ autoload :SchemaRegistry, "#{__dir__}/validation/schema_registry"
17
+ autoload :VerifyOrchestrator,
18
+ "#{__dir__}/validation/verify_orchestrator"
19
+ autoload :Rules, "#{__dir__}/validation/rules"
20
+ autoload :Report, "#{__dir__}/validation/report"
16
21
  end
17
22
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uniword
4
- VERSION = "1.2.5"
4
+ VERSION = "1.4.0"
5
5
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require "uniword/ooxml/types/relationship_id"
5
4
 
6
5
  module Uniword
7
6
  module Vml
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "../properties/relationship_id"
5
4
 
6
5
  module Uniword
7
6
  module Wordprocessingml
@@ -16,8 +16,8 @@ module Uniword
16
16
  xml do
17
17
  element "delText"
18
18
  namespace Uniword::Ooxml::Namespaces::WordProcessingML
19
- mixed_content
20
19
 
20
+ map_content to: :content
21
21
  map_attribute "xml:space", to: :xml_space, render_nil: false
22
22
  end
23
23