svg_conform 0.1.4 → 0.1.6

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +18 -40
  3. data/README.adoc +111 -1171
  4. data/config/profiles/metanorma.yml +5 -0
  5. data/docs/api_reference.adoc +1355 -0
  6. data/docs/cli_guide.adoc +846 -0
  7. data/docs/reference_manifest.adoc +371 -0
  8. data/docs/requirements.adoc +68 -1
  9. data/examples/document_input_demo.rb +102 -0
  10. data/lib/svg_conform/document.rb +40 -1
  11. data/lib/svg_conform/profile.rb +15 -9
  12. data/lib/svg_conform/references/base_reference.rb +130 -0
  13. data/lib/svg_conform/references/id_definition.rb +38 -0
  14. data/lib/svg_conform/references/reference_classifier.rb +45 -0
  15. data/lib/svg_conform/references/reference_manifest.rb +129 -0
  16. data/lib/svg_conform/references.rb +11 -0
  17. data/lib/svg_conform/remediations/namespace_attribute_remediation.rb +34 -43
  18. data/lib/svg_conform/requirements/id_collection_requirement.rb +38 -0
  19. data/lib/svg_conform/requirements/id_reference_requirement.rb +11 -0
  20. data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +3 -0
  21. data/lib/svg_conform/requirements/link_validation_requirement.rb +114 -31
  22. data/lib/svg_conform/requirements/no_external_css_requirement.rb +5 -2
  23. data/lib/svg_conform/requirements.rb +11 -9
  24. data/lib/svg_conform/sax_validation_handler.rb +16 -1
  25. data/lib/svg_conform/validation_context.rb +67 -1
  26. data/lib/svg_conform/validation_result.rb +43 -2
  27. data/lib/svg_conform/validator.rb +56 -16
  28. data/lib/svg_conform/version.rb +1 -1
  29. data/lib/svg_conform.rb +11 -2
  30. data/spec/svg_conform/commands/svgcheck_compare_command_spec.rb +1 -0
  31. data/spec/svg_conform/commands/svgcheck_compatibility_command_spec.rb +1 -0
  32. data/spec/svg_conform/commands/svgcheck_generate_command_spec.rb +1 -0
  33. data/spec/svg_conform/references/integration_spec.rb +206 -0
  34. data/spec/svg_conform/references/reference_classifier_spec.rb +142 -0
  35. data/spec/svg_conform/references/reference_manifest_spec.rb +307 -0
  36. data/spec/svg_conform/requirements/id_reference_state_spec.rb +93 -0
  37. data/spec/svg_conform/validator_input_types_spec.rb +172 -0
  38. data/svg_conform.gemspec +1 -1
  39. metadata +25 -4
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "IdReferenceRequirement" do
6
+ let(:validator) { SvgConform::Validator.new(mode: :sax) }
7
+
8
+ # SVG with a reference to undefined ID 'missing_id'
9
+ let(:svg_with_error) do
10
+ <<~SVG
11
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
12
+ <a href="#missing_id">
13
+ <rect x="10" y="10" width="80" height="80"/>
14
+ </a>
15
+ </svg>
16
+ SVG
17
+ end
18
+
19
+ # SVG with valid references only
20
+ let(:svg_without_error) do
21
+ <<~SVG
22
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
23
+ <rect id="valid_id" x="10" y="10" width="80" height="80"/>
24
+ <a href="#valid_id">
25
+ <text x="50" y="50">Link</text>
26
+ </a>
27
+ </svg>
28
+ SVG
29
+ end
30
+
31
+ it "does not leak state from first validation to second validation" do
32
+ # First validation: SVG with reference error
33
+ result1 = validator.validate(svg_with_error, profile: :metanorma)
34
+
35
+ # Verify first validation caught the error
36
+ expect(result1.errors.map(&:message)).to include(
37
+ match(/Reference to undefined ID 'missing_id'/),
38
+ )
39
+
40
+ # Second validation: SVG without reference errors
41
+ result2 = validator.validate(svg_without_error, profile: :metanorma)
42
+
43
+ # Verify second validation does NOT report errors from first validation
44
+ error_messages = result2.errors.map(&:message).join("\n")
45
+ expect(error_messages).not_to include("missing_id"),
46
+ "State leaked from first validation! Second validation should not mention 'missing_id'"
47
+
48
+ # Second validation should have no errors (or at least no reference errors)
49
+ reference_errors = result2.errors.select do |e|
50
+ e.message.include?("Reference to undefined")
51
+ end
52
+ expect(reference_errors).to be_empty,
53
+ "Second validation should have no reference errors, but got: #{reference_errors.map(&:message)}"
54
+ end
55
+
56
+ it "handles multiple sequential validations correctly" do
57
+ # Run multiple validations to ensure state resets each time
58
+ results = []
59
+
60
+ # Alternate between error and no-error SVGs
61
+ 3.times do
62
+ results << validator.validate(svg_with_error, profile: :metanorma)
63
+ results << validator.validate(svg_without_error, profile: :metanorma)
64
+ end
65
+
66
+ # Check that error pattern is consistent
67
+ results.each_with_index do |result, i|
68
+ if i.even?
69
+ # Should have error (svg_with_error)
70
+ expect(result.errors.map(&:message)).to include(
71
+ match(/Reference to undefined ID 'missing_id'/),
72
+ )
73
+ else
74
+ # Should NOT have error about missing_id (svg_without_error)
75
+ error_messages = result.errors.map(&:message).join("\n")
76
+ expect(error_messages).not_to include("missing_id")
77
+ end
78
+ end
79
+ end
80
+
81
+ it "resets state when using the same profile instance" do
82
+ # Get profile instance
83
+ profile = SvgConform::Profiles.get(:metanorma)
84
+
85
+ # Run two validations using the same profile
86
+ validator.validate(svg_with_error, profile: profile)
87
+ result2 = validator.validate(svg_without_error, profile: profile)
88
+
89
+ # Second validation should not have first validation's errors
90
+ error_messages = result2.errors.map(&:message).join("\n")
91
+ expect(error_messages).not_to include("missing_id")
92
+ end
93
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe SvgConform::Validator do
6
+ let(:validator) { described_class.new(mode: :sax) }
7
+ let(:simple_svg) do
8
+ <<~SVG
9
+ <?xml version="1.0" encoding="UTF-8"?>
10
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
11
+ <rect id="box" x="10" y="10" width="80" height="80" fill="black"/>
12
+ </svg>
13
+ SVG
14
+ end
15
+
16
+ describe "#validate with different input types" do
17
+ context "with String input (backward compatibility)" do
18
+ it "validates XML string successfully" do
19
+ result = validator.validate(simple_svg, profile: :metanorma)
20
+
21
+ expect(result).to be_a(SvgConform::ValidationResult)
22
+ expect(result.valid?).to be true
23
+ end
24
+
25
+ it "processes errors correctly" do
26
+ invalid_svg = '<svg><rect fill="red"/></svg>'
27
+ result = validator.validate(invalid_svg, profile: :svg_1_2_rfc)
28
+
29
+ expect(result).to be_a(SvgConform::ValidationResult)
30
+ # Will have errors due to missing viewBox and invalid color
31
+ end
32
+ end
33
+
34
+ context "with Moxml::Document input" do
35
+ it "validates Moxml document without re-parsing" do
36
+ # Parse once into Moxml
37
+ moxml_doc = Moxml.new.parse(simple_svg)
38
+
39
+ # Validate using the document object
40
+ result = validator.validate(moxml_doc, profile: :metanorma)
41
+
42
+ expect(result).to be_a(SvgConform::ValidationResult)
43
+ expect(result.valid?).to be true
44
+ end
45
+
46
+ it "serializes document once for SAX validation (safe for large files)" do
47
+ moxml_doc = Moxml.new.parse(simple_svg)
48
+
49
+ # Should call to_xml once for SAX validation
50
+ # (SAX mode is safe for large files, unlike DOM)
51
+ expect(moxml_doc).to receive(:to_xml).once.and_call_original
52
+
53
+ validator.validate(moxml_doc, profile: :metanorma)
54
+ end
55
+ end
56
+
57
+ context "with Moxml::Element input" do
58
+ it "validates Moxml element node" do
59
+ moxml_doc = Moxml.new.parse(simple_svg)
60
+ svg_element = moxml_doc.root
61
+
62
+ result = validator.validate(svg_element, profile: :metanorma)
63
+
64
+ expect(result).to be_a(SvgConform::ValidationResult)
65
+ expect(result.valid?).to be true
66
+ end
67
+ end
68
+
69
+ context "with Nokogiri input (metanorm use case)" do
70
+ it "validates Nokogiri::XML::Document" do
71
+ nokogiri_doc = Nokogiri::XML(simple_svg)
72
+
73
+ result = validator.validate(nokogiri_doc, profile: :metanorma)
74
+
75
+ expect(result).to be_a(SvgConform::ValidationResult)
76
+ expect(result.valid?).to be true
77
+ end
78
+
79
+ it "validates Nokogiri::XML::Element" do
80
+ nokogiri_doc = Nokogiri::XML(simple_svg)
81
+ svg_element = nokogiri_doc.root
82
+
83
+ # This is the metanorma use case - passing element directly
84
+ result = validator.validate(svg_element, profile: :metanorma)
85
+
86
+ expect(result).to be_a(SvgConform::ValidationResult)
87
+ expect(result.valid?).to be true
88
+ end
89
+
90
+ it "serializes Nokogiri element once for SAX validation" do
91
+ nokogiri_doc = Nokogiri::XML(simple_svg)
92
+ svg_element = nokogiri_doc.root
93
+
94
+ # Should call to_xml once for SAX validation
95
+ # (SAX mode is memory-safe for large SVGs)
96
+ expect(svg_element).to receive(:to_xml).once.and_call_original
97
+
98
+ validator.validate(svg_element, profile: :metanorma)
99
+ end
100
+ end
101
+
102
+ context "with invalid input" do
103
+ it "raises ArgumentError for unsupported input type" do
104
+ expect do
105
+ validator.validate(12345, profile: :metanorma)
106
+ end.to raise_error(ArgumentError, /Invalid input type/)
107
+ end
108
+
109
+ it "raises ArgumentError for nil input" do
110
+ expect do
111
+ validator.validate(nil, profile: :metanorma)
112
+ end.to raise_error(ArgumentError, /Invalid input type/)
113
+ end
114
+ end
115
+
116
+ context "with reference manifest" do
117
+ let(:svg_with_refs) do
118
+ <<~SVG
119
+ <?xml version="1.0" encoding="UTF-8"?>
120
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
121
+ <defs>
122
+ <rect id="box" width="10" height="10"/>
123
+ </defs>
124
+ <use href="#box" x="20" y="20"/>
125
+ </svg>
126
+ SVG
127
+ end
128
+
129
+ it "builds reference manifest from Nokogiri element" do
130
+ nokogiri_doc = Nokogiri::XML(svg_with_refs)
131
+ svg_element = nokogiri_doc.root
132
+
133
+ result = validator.validate(svg_element, profile: :metanorma)
134
+
135
+ expect(result.reference_manifest).not_to be_nil
136
+ expect(result.available_ids.map(&:id_value)).to include("box")
137
+ expect(result.internal_references.size).to be > 0
138
+ end
139
+
140
+ it "builds reference manifest from Moxml document" do
141
+ moxml_doc = Moxml.new.parse(svg_with_refs)
142
+
143
+ result = validator.validate(moxml_doc, profile: :metanorma)
144
+
145
+ expect(result.reference_manifest).not_to be_nil
146
+ expect(result.available_ids.map(&:id_value)).to include("box")
147
+ end
148
+ end
149
+ end
150
+
151
+ describe "integration with remediation" do
152
+ let(:invalid_svg) do
153
+ <<~SVG
154
+ <?xml version="1.0" encoding="UTF-8"?>
155
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
156
+ <rect fill="red" width="10" height="10"/>
157
+ </svg>
158
+ SVG
159
+ end
160
+
161
+ it "applies remediation when using document object input" do
162
+ nokogiri_doc = Nokogiri::XML(invalid_svg)
163
+ svg_element = nokogiri_doc.root
164
+
165
+ result = validator.validate(svg_element, profile: :svg_1_2_rfc, fix: true)
166
+
167
+ # Should have validation errors initially
168
+ expect(result.has_errors?).to be true
169
+ # Remediation should have been attempted
170
+ end
171
+ end
172
+ end
data/svg_conform.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ["lib"]
30
30
 
31
31
  spec.add_dependency "lutaml-model", "~> 0.7"
32
- spec.add_dependency "moxml"
32
+ spec.add_dependency "moxml", "~> 0.1", ">= 0.1.10"
33
33
  spec.add_dependency "table_tennis"
34
34
  spec.add_dependency "thor"
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: svg_conform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-19 00:00:00.000000000 Z
11
+ date: 2025-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: moxml
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
31
34
  - - ">="
32
35
  - !ruby/object:Gem::Version
33
- version: '0'
36
+ version: 0.1.10
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '0.1'
38
44
  - - ">="
39
45
  - !ruby/object:Gem::Version
40
- version: '0'
46
+ version: 0.1.10
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: table_tennis
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -92,11 +98,15 @@ files:
92
98
  - config/profiles/svg_1_2_rfc.yml
93
99
  - config/profiles/svg_1_2_rfc_with_rdf.yml
94
100
  - config/svgcheck_mapping.yml
101
+ - docs/api_reference.adoc
102
+ - docs/cli_guide.adoc
95
103
  - docs/profiles.adoc
96
104
  - docs/rdf_metadata_support.adoc
105
+ - docs/reference_manifest.adoc
97
106
  - docs/remediation.adoc
98
107
  - docs/requirements.adoc
99
108
  - examples/demo.rb
109
+ - examples/document_input_demo.rb
100
110
  - examples/readme_usage.rb
101
111
  - examples/requirements_demo.rb
102
112
  - exe/svg_conform
@@ -137,6 +147,11 @@ files:
137
147
  - lib/svg_conform/node_index_builder.rb
138
148
  - lib/svg_conform/profile.rb
139
149
  - lib/svg_conform/profiles.rb
150
+ - lib/svg_conform/references.rb
151
+ - lib/svg_conform/references/base_reference.rb
152
+ - lib/svg_conform/references/id_definition.rb
153
+ - lib/svg_conform/references/reference_classifier.rb
154
+ - lib/svg_conform/references/reference_manifest.rb
140
155
  - lib/svg_conform/remediation_engine.rb
141
156
  - lib/svg_conform/remediation_result.rb
142
157
  - lib/svg_conform/remediation_runner.rb
@@ -160,6 +175,7 @@ files:
160
175
  - lib/svg_conform/requirements/element_requirement_config.rb
161
176
  - lib/svg_conform/requirements/font_family_requirement.rb
162
177
  - lib/svg_conform/requirements/forbidden_content_requirement.rb
178
+ - lib/svg_conform/requirements/id_collection_requirement.rb
163
179
  - lib/svg_conform/requirements/id_reference_requirement.rb
164
180
  - lib/svg_conform/requirements/invalid_id_references_requirement.rb
165
181
  - lib/svg_conform/requirements/link_validation_requirement.rb
@@ -386,6 +402,9 @@ files:
386
402
  - spec/svg_conform/profiles/no_external_css_profile_spec.rb
387
403
  - spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb
388
404
  - spec/svg_conform/profiles/svg_1_2_rfc_with_rdf_profile_spec.rb
405
+ - spec/svg_conform/references/integration_spec.rb
406
+ - spec/svg_conform/references/reference_classifier_spec.rb
407
+ - spec/svg_conform/references/reference_manifest_spec.rb
389
408
  - spec/svg_conform/remediations/color_remediation_spec.rb
390
409
  - spec/svg_conform/remediations/font_embedding_remediation_spec.rb
391
410
  - spec/svg_conform/remediations/font_remediation_spec.rb
@@ -401,6 +420,7 @@ files:
401
420
  - spec/svg_conform/requirements/font_family_requirement_spec.rb
402
421
  - spec/svg_conform/requirements/forbidden_content_requirement_spec.rb
403
422
  - spec/svg_conform/requirements/id_reference_requirement_spec.rb
423
+ - spec/svg_conform/requirements/id_reference_state_spec.rb
404
424
  - spec/svg_conform/requirements/invalid_id_references_requirement_spec.rb
405
425
  - spec/svg_conform/requirements/link_validation_requirement_spec.rb
406
426
  - spec/svg_conform/requirements/namespace_attributes_requirement_spec.rb
@@ -411,6 +431,7 @@ files:
411
431
  - spec/svg_conform/requirements/style_promotion_requirement_spec.rb
412
432
  - spec/svg_conform/requirements/style_requirement_spec.rb
413
433
  - spec/svg_conform/requirements/viewbox_required_requirement_spec.rb
434
+ - spec/svg_conform/validator_input_types_spec.rb
414
435
  - spec/svg_conform_spec.rb
415
436
  - spec/svgcheck_compatibility_spec.rb
416
437
  - svg_conform.gemspec