svg_conform 0.1.4 → 0.1.5
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/.rubocop_todo.yml +182 -21
- data/README.adoc +391 -989
- data/config/profiles/metanorma.yml +5 -0
- data/docs/api_reference.adoc +1355 -0
- data/docs/cli_guide.adoc +846 -0
- data/docs/reference_manifest.adoc +370 -0
- data/docs/requirements.adoc +68 -1
- data/examples/document_input_demo.rb +102 -0
- data/lib/svg_conform/document.rb +40 -1
- data/lib/svg_conform/profile.rb +15 -9
- data/lib/svg_conform/references/base_reference.rb +130 -0
- data/lib/svg_conform/references/id_definition.rb +38 -0
- data/lib/svg_conform/references/reference_classifier.rb +45 -0
- data/lib/svg_conform/references/reference_manifest.rb +129 -0
- data/lib/svg_conform/references.rb +11 -0
- data/lib/svg_conform/remediations/namespace_attribute_remediation.rb +34 -43
- data/lib/svg_conform/requirements/id_collection_requirement.rb +38 -0
- data/lib/svg_conform/requirements/id_reference_requirement.rb +11 -0
- data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +3 -0
- data/lib/svg_conform/requirements/link_validation_requirement.rb +114 -31
- data/lib/svg_conform/requirements/no_external_css_requirement.rb +5 -2
- data/lib/svg_conform/requirements.rb +11 -9
- data/lib/svg_conform/sax_validation_handler.rb +16 -1
- data/lib/svg_conform/validation_context.rb +67 -1
- data/lib/svg_conform/validation_result.rb +43 -2
- data/lib/svg_conform/validator.rb +56 -16
- data/lib/svg_conform/version.rb +1 -1
- data/lib/svg_conform.rb +11 -2
- data/spec/svg_conform/commands/svgcheck_compare_command_spec.rb +1 -0
- data/spec/svg_conform/commands/svgcheck_compatibility_command_spec.rb +1 -0
- data/spec/svg_conform/commands/svgcheck_generate_command_spec.rb +1 -0
- data/spec/svg_conform/references/integration_spec.rb +206 -0
- data/spec/svg_conform/references/reference_classifier_spec.rb +142 -0
- data/spec/svg_conform/references/reference_manifest_spec.rb +307 -0
- data/spec/svg_conform/requirements/id_reference_state_spec.rb +93 -0
- data/spec/svg_conform/validator_input_types_spec.rb +172 -0
- metadata +17 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "base_requirement"
|
|
4
|
+
require_relative "../references/reference_classifier"
|
|
4
5
|
|
|
5
6
|
module SvgConform
|
|
6
7
|
module Requirements
|
|
@@ -9,22 +10,26 @@ module SvgConform
|
|
|
9
10
|
return unless element?(node)
|
|
10
11
|
|
|
11
12
|
# Check href attributes (both href and xlink:href)
|
|
12
|
-
href_value =
|
|
13
|
+
href_value = extract_href(node)
|
|
14
|
+
return unless href_value
|
|
13
15
|
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
xlink_href = node.attributes.find do |attr|
|
|
17
|
-
attr.name == "href" && attr.namespace&.uri == "http://www.w3.org/1999/xlink"
|
|
18
|
-
end
|
|
19
|
-
href_value = xlink_href&.value
|
|
20
|
-
end
|
|
16
|
+
# Always validate ASCII requirement (applies to all references)
|
|
17
|
+
validate_ascii(href_value, node, context)
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
# Classify and register the reference
|
|
20
|
+
reference = classify_reference(href_value, node)
|
|
21
|
+
return unless reference
|
|
22
|
+
|
|
23
|
+
context.register_reference(reference)
|
|
24
|
+
|
|
25
|
+
# Only validate internal references
|
|
26
|
+
if reference.internally_validatable?
|
|
27
|
+
validate_internal_reference(reference, node, context)
|
|
28
|
+
elsif reference.requires_consumer_validation?
|
|
29
|
+
# Don't error - just notify consumer
|
|
30
|
+
context.add_external_reference_notice(
|
|
26
31
|
node: node,
|
|
27
|
-
|
|
32
|
+
reference: reference,
|
|
28
33
|
)
|
|
29
34
|
end
|
|
30
35
|
|
|
@@ -34,14 +39,11 @@ module SvgConform
|
|
|
34
39
|
iri_value = get_attribute(node, attr_name)
|
|
35
40
|
next unless iri_value
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
validate_ascii_attribute(iri_value, attr_name, node, context)
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
node: node,
|
|
43
|
-
severity: :error,
|
|
44
|
-
)
|
|
44
|
+
# Classify and register these references too
|
|
45
|
+
iri_ref = classify_reference(iri_value, node, attr_name)
|
|
46
|
+
context.register_reference(iri_ref) if iri_ref
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
49
|
|
|
@@ -49,13 +51,30 @@ module SvgConform
|
|
|
49
51
|
# Check href attributes
|
|
50
52
|
href_value = element.raw_attributes["href"] || element.raw_attributes["xlink:href"]
|
|
51
53
|
|
|
52
|
-
if href_value
|
|
53
|
-
context
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
if href_value
|
|
55
|
+
validate_ascii(href_value, element, context)
|
|
56
|
+
|
|
57
|
+
# Classify and register
|
|
58
|
+
reference = References::ReferenceClassifier.classify(
|
|
59
|
+
href_value,
|
|
60
|
+
element_name: element.name,
|
|
61
|
+
attribute_name: "href",
|
|
62
|
+
line_number: element.line,
|
|
63
|
+
column_number: element.column,
|
|
58
64
|
)
|
|
65
|
+
|
|
66
|
+
if reference
|
|
67
|
+
context.register_reference(reference)
|
|
68
|
+
|
|
69
|
+
if reference.internally_validatable?
|
|
70
|
+
validate_internal_reference(reference, element, context)
|
|
71
|
+
elsif reference.requires_consumer_validation?
|
|
72
|
+
context.add_external_reference_notice(
|
|
73
|
+
node: element,
|
|
74
|
+
reference: reference,
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
59
78
|
end
|
|
60
79
|
|
|
61
80
|
# Check other IRI attributes
|
|
@@ -64,19 +83,83 @@ module SvgConform
|
|
|
64
83
|
iri_value = element.raw_attributes[attr_name]
|
|
65
84
|
next unless iri_value
|
|
66
85
|
|
|
67
|
-
|
|
86
|
+
validate_ascii_attribute(iri_value, attr_name, element, context)
|
|
68
87
|
|
|
88
|
+
# Classify and register
|
|
89
|
+
iri_ref = References::ReferenceClassifier.classify(
|
|
90
|
+
iri_value,
|
|
91
|
+
element_name: element.name,
|
|
92
|
+
attribute_name: attr_name,
|
|
93
|
+
line_number: element.line,
|
|
94
|
+
column_number: element.column,
|
|
95
|
+
)
|
|
96
|
+
context.register_reference(iri_ref) if iri_ref
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def extract_href(node)
|
|
103
|
+
href_value = get_attribute(node, "href")
|
|
104
|
+
return href_value if href_value
|
|
105
|
+
|
|
106
|
+
# Check for xlink:href if regular href is not present
|
|
107
|
+
if node.respond_to?(:attributes)
|
|
108
|
+
xlink_href = node.attributes.find do |attr|
|
|
109
|
+
attr.name == "href" && attr.namespace&.uri == "http://www.w3.org/1999/xlink"
|
|
110
|
+
end
|
|
111
|
+
xlink_href&.value
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def classify_reference(href_value, node, attr_name = "href")
|
|
116
|
+
References::ReferenceClassifier.classify(
|
|
117
|
+
href_value,
|
|
118
|
+
element_name: node.name,
|
|
119
|
+
attribute_name: attr_name,
|
|
120
|
+
line_number: node.respond_to?(:line) ? node.line : nil,
|
|
121
|
+
column_number: node.respond_to?(:column) ? node.column : nil,
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def validate_ascii(href_value, node, context)
|
|
126
|
+
return if ascii_only?(href_value)
|
|
127
|
+
|
|
128
|
+
context.add_error(
|
|
129
|
+
requirement_id: id,
|
|
130
|
+
message: "Link href '#{href_value}' contains non-ASCII characters",
|
|
131
|
+
node: node,
|
|
132
|
+
severity: :error,
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def validate_ascii_attribute(iri_value, attr_name, node, context)
|
|
137
|
+
return if ascii_only?(iri_value)
|
|
138
|
+
|
|
139
|
+
context.add_error(
|
|
140
|
+
requirement_id: id,
|
|
141
|
+
message: "IRI attribute '#{attr_name}' value '#{iri_value}' contains non-ASCII characters",
|
|
142
|
+
node: node,
|
|
143
|
+
severity: :error,
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def validate_internal_reference(reference, node, context)
|
|
148
|
+
return unless reference.is_a?(References::InternalFragmentReference)
|
|
149
|
+
|
|
150
|
+
target_id = reference.target_id
|
|
151
|
+
|
|
152
|
+
# Use manifest to check if ID exists
|
|
153
|
+
unless context.id_defined?(target_id)
|
|
69
154
|
context.add_error(
|
|
70
155
|
requirement_id: id,
|
|
71
|
-
message: "
|
|
72
|
-
node:
|
|
156
|
+
message: "Internal reference '#{reference.value}' points to non-existent element",
|
|
157
|
+
node: node,
|
|
73
158
|
severity: :error,
|
|
74
159
|
)
|
|
75
160
|
end
|
|
76
161
|
end
|
|
77
162
|
|
|
78
|
-
private
|
|
79
|
-
|
|
80
163
|
def ascii_only?(string)
|
|
81
164
|
string.ascii_only?
|
|
82
165
|
end
|
|
@@ -41,10 +41,10 @@ module SvgConform
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def needs_deferred_validation?
|
|
44
|
-
check_style_elements
|
|
44
|
+
check_style_elements # Only deferred if checking style elements
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
def collect_sax_data(element,
|
|
47
|
+
def collect_sax_data(element, _context)
|
|
48
48
|
# Collect style elements for deferred validation (text content needs to be complete)
|
|
49
49
|
if check_style_elements && element.name == "style"
|
|
50
50
|
@collected_style_elements << element
|
|
@@ -52,6 +52,9 @@ module SvgConform
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def validate_sax_complete(context)
|
|
55
|
+
# Guard against nil collection
|
|
56
|
+
return unless @collected_style_elements
|
|
57
|
+
|
|
55
58
|
# Validate collected style elements
|
|
56
59
|
@collected_style_elements.each do |element|
|
|
57
60
|
check_style_element_sax(element, context)
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "requirements/base_requirement"
|
|
4
|
+
require_relative "requirements/element_requirement_config"
|
|
4
5
|
require_relative "requirements/allowed_elements_requirement"
|
|
5
|
-
require_relative "requirements/color_restrictions_requirement"
|
|
6
|
-
require_relative "requirements/font_family_requirement"
|
|
7
|
-
require_relative "requirements/invalid_id_references_requirement"
|
|
8
6
|
require_relative "requirements/namespace_requirement"
|
|
9
|
-
require_relative "requirements/no_external_css_requirement"
|
|
10
|
-
require_relative "requirements/no_external_fonts_requirement"
|
|
11
|
-
require_relative "requirements/no_external_images_requirement"
|
|
12
|
-
require_relative "requirements/viewbox_required_requirement"
|
|
13
7
|
require_relative "requirements/namespace_attributes_requirement"
|
|
8
|
+
require_relative "requirements/viewbox_required_requirement"
|
|
9
|
+
require_relative "requirements/link_validation_requirement"
|
|
10
|
+
require_relative "requirements/id_reference_requirement"
|
|
11
|
+
require_relative "requirements/id_collection_requirement"
|
|
12
|
+
require_relative "requirements/invalid_id_references_requirement"
|
|
14
13
|
require_relative "requirements/forbidden_content_requirement"
|
|
14
|
+
require_relative "requirements/color_restrictions_requirement"
|
|
15
|
+
require_relative "requirements/font_family_requirement"
|
|
15
16
|
require_relative "requirements/style_requirement"
|
|
16
17
|
require_relative "requirements/style_promotion_requirement"
|
|
17
|
-
require_relative "requirements/
|
|
18
|
-
require_relative "requirements/
|
|
18
|
+
require_relative "requirements/no_external_css_requirement"
|
|
19
|
+
require_relative "requirements/no_external_fonts_requirement"
|
|
20
|
+
require_relative "requirements/no_external_images_requirement"
|
|
19
21
|
|
|
20
22
|
module SvgConform
|
|
21
23
|
module Requirements
|
|
@@ -4,6 +4,7 @@ require "nokogiri"
|
|
|
4
4
|
require_relative "element_proxy"
|
|
5
5
|
require_relative "validation_context"
|
|
6
6
|
require_relative "validation_result"
|
|
7
|
+
require_relative "references"
|
|
7
8
|
|
|
8
9
|
module SvgConform
|
|
9
10
|
# SAX event handler for streaming SVG validation
|
|
@@ -30,6 +31,9 @@ module SvgConform
|
|
|
30
31
|
|
|
31
32
|
# SAX Event: Document start
|
|
32
33
|
def start_document
|
|
34
|
+
# Reset state in requirements that maintain state across validations
|
|
35
|
+
reset_stateful_requirements
|
|
36
|
+
|
|
33
37
|
# Initialize root level counters
|
|
34
38
|
@position_counters.push({})
|
|
35
39
|
end
|
|
@@ -143,12 +147,14 @@ module SvgConform
|
|
|
143
147
|
context.instance_variable_set(:@structurally_invalid_node_ids, Set.new)
|
|
144
148
|
context.instance_variable_set(:@node_id_cache, {})
|
|
145
149
|
context.instance_variable_set(:@cache_populated, true) # Skip population for SAX
|
|
150
|
+
context.instance_variable_set(:@reference_manifest,
|
|
151
|
+
References::ReferenceManifest.new(source_document: nil))
|
|
146
152
|
context
|
|
147
153
|
end
|
|
148
154
|
|
|
149
155
|
# Classify requirements based on validation needs
|
|
150
156
|
def classify_requirements
|
|
151
|
-
return unless @profile
|
|
157
|
+
return unless @profile&.requirements
|
|
152
158
|
|
|
153
159
|
@profile.requirements.each do |req|
|
|
154
160
|
if req.respond_to?(:needs_deferred_validation?) && req.needs_deferred_validation?
|
|
@@ -158,5 +164,14 @@ module SvgConform
|
|
|
158
164
|
end
|
|
159
165
|
end
|
|
160
166
|
end
|
|
167
|
+
|
|
168
|
+
# Reset state in requirements that maintain state
|
|
169
|
+
def reset_stateful_requirements
|
|
170
|
+
return unless @profile&.requirements
|
|
171
|
+
|
|
172
|
+
@profile.requirements.each do |req|
|
|
173
|
+
req.reset_state if req.respond_to?(:reset_state)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
161
176
|
end
|
|
162
177
|
end
|
|
@@ -6,7 +6,7 @@ module SvgConform
|
|
|
6
6
|
# Context object passed to rules during validation
|
|
7
7
|
class ValidationContext
|
|
8
8
|
attr_reader :document, :profile, :errors, :warnings, :fixes,
|
|
9
|
-
:validity_errors
|
|
9
|
+
:validity_errors, :reference_manifest
|
|
10
10
|
|
|
11
11
|
def initialize(document, profile)
|
|
12
12
|
@document = document
|
|
@@ -19,6 +19,9 @@ module SvgConform
|
|
|
19
19
|
@structurally_invalid_node_ids = Set.new
|
|
20
20
|
@node_id_cache = {}
|
|
21
21
|
@cache_populated = false
|
|
22
|
+
@reference_manifest = References::ReferenceManifest.new(
|
|
23
|
+
source_document: document.file_path,
|
|
24
|
+
)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
# Mark a node as structurally invalid (e.g., invalid parent-child relationship)
|
|
@@ -128,6 +131,39 @@ requirement_id: nil, severity: nil, fix: nil, data: {})
|
|
|
128
131
|
@data[key]
|
|
129
132
|
end
|
|
130
133
|
|
|
134
|
+
# Register an ID definition
|
|
135
|
+
def register_id(id_value, element_name:, line_number: nil,
|
|
136
|
+
column_number: nil)
|
|
137
|
+
@reference_manifest.register_id(
|
|
138
|
+
id_value,
|
|
139
|
+
element_name: element_name,
|
|
140
|
+
line_number: line_number,
|
|
141
|
+
column_number: column_number,
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Register a reference
|
|
146
|
+
def register_reference(reference)
|
|
147
|
+
@reference_manifest.register_reference(reference)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Check if ID exists in manifest
|
|
151
|
+
def id_defined?(id_value)
|
|
152
|
+
@reference_manifest.id_defined?(id_value)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Add notice for external references (not errors)
|
|
156
|
+
def add_external_reference_notice(node:, reference:, message: nil)
|
|
157
|
+
notice = ValidationNotice.new(
|
|
158
|
+
type: :external_reference,
|
|
159
|
+
node: node,
|
|
160
|
+
message: message || "External reference: #{reference.value}",
|
|
161
|
+
data: { reference: reference },
|
|
162
|
+
)
|
|
163
|
+
@infos << notice
|
|
164
|
+
notice
|
|
165
|
+
end
|
|
166
|
+
|
|
131
167
|
# Generate a unique identifier for a node based on its path
|
|
132
168
|
# Builds a stable path by walking up the parent chain
|
|
133
169
|
# OPTIMIZED: Lazy cache population - populate entire cache on first call
|
|
@@ -489,4 +525,34 @@ requirement_id: nil, severity: nil, violation_type: nil)
|
|
|
489
525
|
end
|
|
490
526
|
end
|
|
491
527
|
end
|
|
528
|
+
|
|
529
|
+
# New class for non-error notifications
|
|
530
|
+
class ValidationNotice
|
|
531
|
+
attr_reader :type, :node, :message, :data
|
|
532
|
+
|
|
533
|
+
def initialize(type:, node:, message:, data: {})
|
|
534
|
+
@type = type
|
|
535
|
+
@node = node
|
|
536
|
+
@message = message
|
|
537
|
+
@data = data
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def line
|
|
541
|
+
@node.respond_to?(:line) ? @node.line : nil
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def column
|
|
545
|
+
@node.respond_to?(:column) ? @node.column : nil
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def to_h
|
|
549
|
+
{
|
|
550
|
+
type: @type,
|
|
551
|
+
message: @message,
|
|
552
|
+
line: line,
|
|
553
|
+
column: column,
|
|
554
|
+
data: @data,
|
|
555
|
+
}
|
|
556
|
+
end
|
|
557
|
+
end
|
|
492
558
|
end
|
|
@@ -6,7 +6,7 @@ module SvgConform
|
|
|
6
6
|
# Represents the result of validating an SVG document
|
|
7
7
|
class ValidationResult
|
|
8
8
|
attr_reader :document, :profile, :errors, :warnings, :validity_errors,
|
|
9
|
-
:fixes_applied, :fixed_document
|
|
9
|
+
:fixes_applied, :fixed_document, :reference_manifest
|
|
10
10
|
|
|
11
11
|
def initialize(document, profile, context)
|
|
12
12
|
@document = document
|
|
@@ -16,6 +16,7 @@ module SvgConform
|
|
|
16
16
|
@validity_errors = context.validity_errors
|
|
17
17
|
@fixes_applied = []
|
|
18
18
|
@fixed_document = nil
|
|
19
|
+
@reference_manifest = context.reference_manifest
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def valid?
|
|
@@ -58,6 +59,31 @@ module SvgConform
|
|
|
58
59
|
@errors.select(&:requirement_id)
|
|
59
60
|
end
|
|
60
61
|
|
|
62
|
+
# Convenience accessors for manifest data
|
|
63
|
+
def available_ids
|
|
64
|
+
@reference_manifest.available_ids
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def internal_references
|
|
68
|
+
@reference_manifest.internal_references
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def external_references
|
|
72
|
+
@reference_manifest.external_references
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def has_external_references?
|
|
76
|
+
!@reference_manifest.external_references.empty?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def unresolved_internal_references
|
|
80
|
+
@reference_manifest.unresolved_internal_references
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def reference_statistics
|
|
84
|
+
@reference_manifest.statistics
|
|
85
|
+
end
|
|
86
|
+
|
|
61
87
|
def apply_fixes
|
|
62
88
|
return self unless fixable?
|
|
63
89
|
|
|
@@ -90,13 +116,14 @@ module SvgConform
|
|
|
90
116
|
|
|
91
117
|
def to_h
|
|
92
118
|
{
|
|
93
|
-
file: @document
|
|
119
|
+
file: @document&.file_path,
|
|
94
120
|
profile: @profile.name,
|
|
95
121
|
valid: valid?,
|
|
96
122
|
errors: @errors.map(&:to_h),
|
|
97
123
|
warnings: @warnings.map(&:to_h),
|
|
98
124
|
fixes_applied: @fixes_applied.size,
|
|
99
125
|
fixable: fixable_count,
|
|
126
|
+
reference_manifest: @reference_manifest.to_h,
|
|
100
127
|
}
|
|
101
128
|
end
|
|
102
129
|
|
|
@@ -104,6 +131,20 @@ module SvgConform
|
|
|
104
131
|
JSON.pretty_generate(to_h, *args)
|
|
105
132
|
end
|
|
106
133
|
|
|
134
|
+
# Export manifest separately for detailed analysis
|
|
135
|
+
def export_manifest(format: :yaml)
|
|
136
|
+
case format
|
|
137
|
+
when :yaml
|
|
138
|
+
@reference_manifest.to_yaml
|
|
139
|
+
when :json
|
|
140
|
+
@reference_manifest.to_json
|
|
141
|
+
when :hash
|
|
142
|
+
@reference_manifest.to_h
|
|
143
|
+
else
|
|
144
|
+
@reference_manifest.to_yaml
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
107
148
|
private
|
|
108
149
|
|
|
109
150
|
def to_text
|
|
@@ -31,25 +31,23 @@ module SvgConform
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# Validate SVG content string
|
|
35
|
-
|
|
34
|
+
# Validate SVG content string or document object
|
|
35
|
+
# Accepts:
|
|
36
|
+
# - String (XML content) → uses SAX for efficient parsing
|
|
37
|
+
# - Moxml::Document or Moxml::Element → serializes once, then SAX validates
|
|
38
|
+
# - Nokogiri::XML::Document or Nokogiri::XML::Element → serializes once, then SAX validates
|
|
39
|
+
# - Any adapter document object (Ox, Oga, REXML, LibXML) → serializes once, then SAX validates
|
|
40
|
+
#
|
|
41
|
+
# IMPORTANT: Always uses SAX validation to safely handle large SVG files.
|
|
42
|
+
# DOM validation can hang on large files, so we serialize once and validate with SAX.
|
|
43
|
+
def validate(input, profile: :svg_1_2_rfc, **options)
|
|
36
44
|
merged_options = @options.merge(options)
|
|
37
|
-
mode = merged_options[:mode]
|
|
38
45
|
|
|
39
|
-
#
|
|
40
|
-
|
|
46
|
+
# Normalize input to string, then use SAX validation
|
|
47
|
+
svg_content = normalize_input_to_string(input)
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
validate_content_sax(svg_content, profile: profile, **merged_options)
|
|
45
|
-
when :dom
|
|
46
|
-
validate_content_dom(svg_content, profile: profile, **merged_options)
|
|
47
|
-
when :auto
|
|
48
|
-
# For content, we can't check file size, so default to SAX
|
|
49
|
-
validate_content_sax(svg_content, profile: profile, **merged_options)
|
|
50
|
-
else
|
|
51
|
-
validate_content_sax(svg_content, profile: profile, **merged_options)
|
|
52
|
-
end
|
|
49
|
+
# Always use SAX mode for safe validation (handles large files)
|
|
50
|
+
validate_content_sax(svg_content, profile: profile, **merged_options)
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
# Validate a Document object (DOM only)
|
|
@@ -87,6 +85,48 @@ module SvgConform
|
|
|
87
85
|
|
|
88
86
|
private
|
|
89
87
|
|
|
88
|
+
# Normalize various input types to XML string
|
|
89
|
+
def normalize_input_to_string(input)
|
|
90
|
+
case input
|
|
91
|
+
when String
|
|
92
|
+
# Already a string, return as-is
|
|
93
|
+
input
|
|
94
|
+
else
|
|
95
|
+
# Try to detect and convert document objects
|
|
96
|
+
convert_document_to_string(input)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Convert document object to XML string
|
|
101
|
+
def convert_document_to_string(input)
|
|
102
|
+
# Check for Moxml document or element
|
|
103
|
+
if input.respond_to?(:to_xml)
|
|
104
|
+
return input.to_xml
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Check for Nokogiri document or element
|
|
108
|
+
if defined?(Nokogiri) && input.is_a?(Nokogiri::XML::Node)
|
|
109
|
+
return input.to_xml
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Check for other adapter documents that respond to to_xml
|
|
113
|
+
if input.respond_to?(:to_s) && xml_like?(input)
|
|
114
|
+
return input.to_s
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# If we can't convert, raise an error
|
|
118
|
+
raise ArgumentError,
|
|
119
|
+
"Invalid input type: #{input.class}. " \
|
|
120
|
+
"Expected String, Moxml document, Nokogiri document, or adapter document object. " \
|
|
121
|
+
"Input must respond to #to_xml or be a valid XML string."
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Check if object looks like an XML document
|
|
125
|
+
def xml_like?(obj)
|
|
126
|
+
# Basic heuristic: check if it has XML-like methods
|
|
127
|
+
obj.respond_to?(:name) && obj.respond_to?(:children)
|
|
128
|
+
end
|
|
129
|
+
|
|
90
130
|
def determine_mode(file_path, requested_mode)
|
|
91
131
|
case requested_mode
|
|
92
132
|
when :sax
|
data/lib/svg_conform/version.rb
CHANGED
data/lib/svg_conform.rb
CHANGED
|
@@ -10,16 +10,25 @@ Lutaml::Model::Config.configure do |config|
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
require_relative "svg_conform/version"
|
|
13
|
+
require_relative "svg_conform/constants"
|
|
14
|
+
require_relative "svg_conform/profile"
|
|
15
|
+
require_relative "svg_conform/profiles"
|
|
13
16
|
require_relative "svg_conform/document"
|
|
14
17
|
require_relative "svg_conform/sax_document"
|
|
18
|
+
require_relative "svg_conform/element_proxy"
|
|
15
19
|
require_relative "svg_conform/validation_context"
|
|
16
20
|
require_relative "svg_conform/validation_result"
|
|
17
|
-
require_relative "svg_conform/
|
|
21
|
+
require_relative "svg_conform/validator"
|
|
22
|
+
require_relative "svg_conform/conformance_report"
|
|
23
|
+
require_relative "svg_conform/batch_report"
|
|
18
24
|
require_relative "svg_conform/requirements"
|
|
25
|
+
require_relative "svg_conform/references"
|
|
19
26
|
require_relative "svg_conform/remediations"
|
|
20
27
|
require_relative "svg_conform/remediation_result"
|
|
28
|
+
require_relative "svg_conform/remediation_engine"
|
|
29
|
+
require_relative "svg_conform/remediation_runner"
|
|
30
|
+
require_relative "svg_conform/fixer"
|
|
21
31
|
require_relative "svg_conform/external_checkers"
|
|
22
|
-
require_relative "svg_conform/cli"
|
|
23
32
|
|
|
24
33
|
module SvgConform
|
|
25
34
|
class Error < StandardError; end
|