svg_conform 0.1.6 → 0.1.7
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/Gemfile +2 -2
- data/lib/svg_conform/document.rb +22 -1
- data/lib/svg_conform/node_helpers.rb +72 -0
- data/lib/svg_conform/remediations/base_remediation.rb +5 -35
- data/lib/svg_conform/remediations/namespace_remediation.rb +60 -0
- data/lib/svg_conform/requirements/base_requirement.rb +5 -41
- data/lib/svg_conform/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a671b1d69dd6498470fc03cdaf2488bd3180187a39ef505cef2cb67d76de1725
|
|
4
|
+
data.tar.gz: c915d4ed0894b35293d78ea4a71f326e8985190dac8ae8df55c55b8eb10d1696
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a32f6b36b517598e1a5f95612457c4e9a5386f64a15d6383dfb2e63052a2c58354a24afc730ad3a27b3ce6165c52447872e759655ef33ba8c546c12de2b16fbc
|
|
7
|
+
data.tar.gz: 1f22474906b51fe86cdeadac2817a66b2695bf7b1685370e25e058ed6c66dc9d09b2f95e26526b5a7937b493f2a7e51b3cf7a97b036c4ec0c8a7f7b0d1494801
|
data/Gemfile
CHANGED
data/lib/svg_conform/document.rb
CHANGED
|
@@ -92,7 +92,28 @@ module SvgConform
|
|
|
92
92
|
def to_xml
|
|
93
93
|
# Always generate from current moxml_document state
|
|
94
94
|
# This ensures modifications are reflected in the output
|
|
95
|
-
@moxml_document.to_xml
|
|
95
|
+
xml = @moxml_document.to_xml
|
|
96
|
+
|
|
97
|
+
# Clean up unused namespace declarations if marked by remediations
|
|
98
|
+
if instance_variable_defined?(:@unused_namespace_prefixes)
|
|
99
|
+
prefixes = @unused_namespace_prefixes
|
|
100
|
+
xml = remove_namespace_declarations(xml, prefixes) if prefixes && !prefixes.empty?
|
|
101
|
+
# Clear the marker after cleanup
|
|
102
|
+
remove_instance_variable(:@unused_namespace_prefixes)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
xml
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def remove_namespace_declarations(xml, prefixes)
|
|
109
|
+
# Remove xmlns:prefix="uri" declarations from the XML string
|
|
110
|
+
# This works around Moxml/Nokogiri's inability to remove namespace declarations
|
|
111
|
+
prefixes.reduce(xml) do |result, prefix|
|
|
112
|
+
# Match xmlns:prefix="..." with various quote styles and surrounding whitespace
|
|
113
|
+
# The pattern matches: xmlns:prefix="uri" or xmlns:prefix='uri'
|
|
114
|
+
# It captures leading whitespace to remove it too
|
|
115
|
+
result.gsub(%r{\s+xmlns:#{prefix}=["'][^"']*["']}, "")
|
|
116
|
+
end
|
|
96
117
|
end
|
|
97
118
|
|
|
98
119
|
def dup
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SvgConform
|
|
4
|
+
# Shared helper methods for working with XML nodes
|
|
5
|
+
#
|
|
6
|
+
# This module provides common utilities used by both requirements
|
|
7
|
+
# and remediations to avoid code duplication and ensure consistency.
|
|
8
|
+
module NodeHelpers
|
|
9
|
+
# Check if a node is an element
|
|
10
|
+
# @param node [Object] the node to check
|
|
11
|
+
# @return [Boolean] true if the node is an element
|
|
12
|
+
def element?(node)
|
|
13
|
+
node.respond_to?(:name) && !node.name.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Check if a node is text
|
|
17
|
+
# @param node [Object] the node to check
|
|
18
|
+
# @return [Boolean] true if the node is text
|
|
19
|
+
def text?(node)
|
|
20
|
+
node.respond_to?(:text?) && node.text?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get attribute value from a node
|
|
24
|
+
# @param node [Object] the node
|
|
25
|
+
# @param name [String] attribute name
|
|
26
|
+
# @return [String, nil] attribute value or nil
|
|
27
|
+
def get_attribute(node, name)
|
|
28
|
+
return nil unless node.respond_to?(:[])
|
|
29
|
+
|
|
30
|
+
node[name]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Set attribute value on a node
|
|
34
|
+
# @param node [Object] the node
|
|
35
|
+
# @param name [String] attribute name
|
|
36
|
+
# @param value [String] attribute value
|
|
37
|
+
# @return [Boolean] true if successful
|
|
38
|
+
def set_attribute(node, name, value)
|
|
39
|
+
return unless node.respond_to?(:[]=)
|
|
40
|
+
|
|
41
|
+
node[name] = value
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if node has an attribute
|
|
46
|
+
# @param node [Object] the node
|
|
47
|
+
# @param name [String] attribute name
|
|
48
|
+
# @return [Boolean] true if attribute exists
|
|
49
|
+
def has_attribute?(node, name)
|
|
50
|
+
return false unless node.respond_to?(:[])
|
|
51
|
+
|
|
52
|
+
!node[name].nil?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Remove attribute from a node
|
|
56
|
+
# @param node [Object] the node
|
|
57
|
+
# @param name [String] attribute name
|
|
58
|
+
# @return [Boolean] true if successful
|
|
59
|
+
def remove_attribute(node, name)
|
|
60
|
+
if node.respond_to?(:remove_attribute)
|
|
61
|
+
node.remove_attribute(name)
|
|
62
|
+
true
|
|
63
|
+
elsif node.respond_to?(:[]=) && node.respond_to?(:[])
|
|
64
|
+
# Fallback for bracket notation
|
|
65
|
+
node[name] = nil
|
|
66
|
+
true
|
|
67
|
+
else
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
4
|
require_relative "../remediation_result"
|
|
5
|
+
require_relative "../node_helpers"
|
|
5
6
|
|
|
6
7
|
module SvgConform
|
|
7
8
|
module Remediations
|
|
8
9
|
# Base class for all remediations using lutaml-model serialization
|
|
9
10
|
class BaseRemediation < Lutaml::Model::Serializable
|
|
11
|
+
include SvgConform::NodeHelpers
|
|
12
|
+
|
|
10
13
|
attribute :id, :string
|
|
11
14
|
attribute :description, :string
|
|
12
15
|
attribute :targets, :string, collection: true
|
|
@@ -72,27 +75,8 @@ module SvgConform
|
|
|
72
75
|
|
|
73
76
|
protected
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def get_attribute(node, attr_name)
|
|
80
|
-
return nil unless node.respond_to?(:[])
|
|
81
|
-
|
|
82
|
-
node[attr_name]
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def set_attribute(node, attr_name, value)
|
|
86
|
-
return unless node.respond_to?(:[]=)
|
|
87
|
-
|
|
88
|
-
node[attr_name] = value
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def has_attribute?(node, attr_name)
|
|
92
|
-
return false unless node.respond_to?(:[])
|
|
93
|
-
|
|
94
|
-
!node[attr_name].nil?
|
|
95
|
-
end
|
|
78
|
+
# Helper methods for node manipulation included via NodeHelpers module:
|
|
79
|
+
# element?, text?, get_attribute, set_attribute, has_attribute?, remove_attribute
|
|
96
80
|
|
|
97
81
|
def find_nodes(document, &)
|
|
98
82
|
nodes = []
|
|
@@ -100,20 +84,6 @@ module SvgConform
|
|
|
100
84
|
nodes
|
|
101
85
|
end
|
|
102
86
|
|
|
103
|
-
# Helper method to remove attribute
|
|
104
|
-
def remove_attribute(node, name)
|
|
105
|
-
if node.respond_to?(:remove_attribute)
|
|
106
|
-
node.remove_attribute(name)
|
|
107
|
-
true
|
|
108
|
-
elsif node.respond_to?(:[]=) && node.respond_to?(:[])
|
|
109
|
-
# Fallback for different implementations
|
|
110
|
-
node[name] = nil
|
|
111
|
-
true
|
|
112
|
-
else
|
|
113
|
-
false
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
87
|
# Helper method to remove node
|
|
118
88
|
def remove_node(node)
|
|
119
89
|
return false unless node.respond_to?(:remove)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "base_remediation"
|
|
4
|
+
require "set"
|
|
4
5
|
|
|
5
6
|
module SvgConform
|
|
6
7
|
module Remediations
|
|
@@ -105,6 +106,11 @@ module SvgConform
|
|
|
105
106
|
remove_node(node)
|
|
106
107
|
end
|
|
107
108
|
|
|
109
|
+
# Clean up unused namespace declarations
|
|
110
|
+
# Note: Moxml/Nokogiri don't support removing namespace declarations directly,
|
|
111
|
+
# so we mark the document for post-processing during serialization
|
|
112
|
+
cleanup_unused_namespace_declarations(document)
|
|
113
|
+
|
|
108
114
|
changes
|
|
109
115
|
end
|
|
110
116
|
|
|
@@ -146,6 +152,60 @@ module SvgConform
|
|
|
146
152
|
# Same logic as find_namespace_uri_for_prefix
|
|
147
153
|
find_namespace_uri_for_prefix(node, prefix)
|
|
148
154
|
end
|
|
155
|
+
|
|
156
|
+
def cleanup_unused_namespace_declarations(document)
|
|
157
|
+
# Find which namespace prefixes are still in use
|
|
158
|
+
used_prefixes = find_used_namespace_prefixes(document)
|
|
159
|
+
|
|
160
|
+
# Get the root element
|
|
161
|
+
root = document.respond_to?(:root) ? document.root : document
|
|
162
|
+
return unless root.respond_to?(:namespace_definitions)
|
|
163
|
+
|
|
164
|
+
# Get disallowed namespace prefixes
|
|
165
|
+
disallowed_prefixes = root.namespace_definitions.filter_map do |ns|
|
|
166
|
+
prefix = ns.respond_to?(:prefix) ? ns.prefix : nil
|
|
167
|
+
# Extract namespace URI from Moxml::Namespace object
|
|
168
|
+
uri = ns.respond_to?(:uri) ? ns.uri : nil
|
|
169
|
+
next if prefix.nil? || prefix.empty? # Skip default namespace
|
|
170
|
+
next if allowed_namespaces.include?(uri) # Keep allowed namespaces
|
|
171
|
+
|
|
172
|
+
next if used_prefixes.include?(prefix) # Keep prefixes still in use
|
|
173
|
+
|
|
174
|
+
prefix
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Store the prefixes to remove on the document for later serialization
|
|
178
|
+
# Try to store on Document wrapper first, fallback to moxml_document
|
|
179
|
+
target = document.respond_to?(:instance_variable_set) ? document : root.document
|
|
180
|
+
target&.instance_variable_set(:@unused_namespace_prefixes, disallowed_prefixes)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def find_used_namespace_prefixes(document)
|
|
184
|
+
used_prefixes = Set.new(["xml"]) # xml prefix is always reserved
|
|
185
|
+
|
|
186
|
+
document.traverse do |node|
|
|
187
|
+
next unless node.respond_to?(:name)
|
|
188
|
+
|
|
189
|
+
# Check element name for namespace prefix
|
|
190
|
+
if node.name.include?(":")
|
|
191
|
+
prefix = node.name.split(":").first
|
|
192
|
+
used_prefixes << prefix
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Check attributes for namespace prefixes
|
|
196
|
+
if node.respond_to?(:attributes)
|
|
197
|
+
node.attributes.each do |attr|
|
|
198
|
+
attr_name = attr.respond_to?(:name) ? attr.name : attr.to_s
|
|
199
|
+
if attr_name.include?(":")
|
|
200
|
+
prefix = attr_name.split(":").first
|
|
201
|
+
used_prefixes << prefix
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
used_prefixes
|
|
208
|
+
end
|
|
149
209
|
end
|
|
150
210
|
end
|
|
151
211
|
end
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../node_helpers"
|
|
4
|
+
|
|
3
5
|
module SvgConform
|
|
4
6
|
module Requirements
|
|
5
7
|
# Base class for all validation requirements
|
|
6
8
|
class BaseRequirement < Lutaml::Model::Serializable
|
|
9
|
+
include SvgConform::NodeHelpers
|
|
10
|
+
|
|
7
11
|
attribute :id, :string
|
|
8
12
|
attribute :description, :string
|
|
9
13
|
attribute :type, :string, polymorphic_class: true, default: -> {
|
|
@@ -65,48 +69,8 @@ module SvgConform
|
|
|
65
69
|
true
|
|
66
70
|
end
|
|
67
71
|
|
|
68
|
-
# Helper method to check if a node is an element
|
|
69
|
-
def element?(node)
|
|
70
|
-
node.respond_to?(:name) && !node.name.nil?
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Helper method to check if a node is text
|
|
74
|
-
def text?(node)
|
|
75
|
-
node.respond_to?(:text?) && node.text?
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Helper method to get attribute value
|
|
79
|
-
def get_attribute(node, name)
|
|
80
|
-
return nil unless node.respond_to?(:attribute)
|
|
81
|
-
|
|
82
|
-
attr = node.attribute(name)
|
|
83
|
-
attr&.value
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Helper method to set attribute value
|
|
87
|
-
def set_attribute(node, name, value)
|
|
88
|
-
return false unless node.respond_to?(:set_attribute)
|
|
89
|
-
|
|
90
|
-
node.set_attribute(name, value)
|
|
91
|
-
true
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Helper method to remove attribute
|
|
95
|
-
def remove_attribute(node, name)
|
|
96
|
-
return false unless node.respond_to?(:remove_attribute)
|
|
97
|
-
|
|
98
|
-
node.remove_attribute(name)
|
|
99
|
-
true
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Helper method to check if attribute exists
|
|
103
|
-
def has_attribute?(node, name)
|
|
104
|
-
return false unless node.respond_to?(:attribute)
|
|
105
|
-
|
|
106
|
-
!node.attribute(name).nil?
|
|
107
|
-
end
|
|
108
|
-
|
|
109
72
|
# Helper method to get all attributes
|
|
73
|
+
# Note: Other attribute helpers are included via NodeHelpers module
|
|
110
74
|
def get_attributes(node)
|
|
111
75
|
return {} unless node.respond_to?(:attributes)
|
|
112
76
|
|
data/lib/svg_conform/version.rb
CHANGED
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
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -144,6 +144,7 @@ files:
|
|
|
144
144
|
- lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb
|
|
145
145
|
- lib/svg_conform/fast_document_analyzer.rb
|
|
146
146
|
- lib/svg_conform/fixer.rb
|
|
147
|
+
- lib/svg_conform/node_helpers.rb
|
|
147
148
|
- lib/svg_conform/node_index_builder.rb
|
|
148
149
|
- lib/svg_conform/profile.rb
|
|
149
150
|
- lib/svg_conform/profiles.rb
|