svg_conform 0.2.4 → 0.2.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/.gitattributes +1 -0
- data/.github/workflows/rake.yml +4 -1
- data/Gemfile +6 -3
- data/Gemfile.lock +211 -0
- data/TODO.issue-100/00-overview.md +135 -0
- data/TODO.issue-100/01-build-lens-core-v1.md +92 -0
- data/TODO.issue-100/02-adopt-lens-core-in-svg.md +123 -0
- data/TODO.issue-100/03-ship-svg-1-1-full-static.md +138 -0
- data/TODO.issue-100/04-ship-svg-2-secure-static.md +131 -0
- data/TODO.issue-100/05-freeze-candidate-and-handoff.md +108 -0
- data/TODO.issue-100/90-progress.md +65 -0
- data/config/profiles/metanorma.yml +4 -4
- data/config/profiles/svg_1_1_full_static.yml +69 -0
- data/config/profiles/svg_2_secure_static.yml +55 -0
- data/config/requirements/processing_features_static.yml +29 -0
- data/data/svg_1_1/manifest.yml +7 -0
- data/data/svg_1_1/svg11-flat-20110816.dtd +5664 -0
- data/docs/api_reference.adoc +19 -1
- data/docs/cli_guide.adoc +35 -45
- data/docs/diagnostics.adoc +73 -0
- data/docs/lens_core_handoff.adoc +84 -0
- data/docs/profiles.adoc +94 -1
- data/lib/svg_conform/cli.rb +3 -4
- data/lib/svg_conform/commands/check.rb +11 -60
- data/lib/svg_conform/conformance_report.rb +79 -1
- data/lib/svg_conform/diagnostics/lens_core_adapter.rb +200 -0
- data/lib/svg_conform/document.rb +12 -1
- data/lib/svg_conform/external_checkers/svgcheck/parser.rb +1 -3
- data/lib/svg_conform/node_helpers.rb +1 -1
- data/lib/svg_conform/profile.rb +11 -2
- data/lib/svg_conform/quality_metrics/complexity_calculator.rb +3 -3
- data/lib/svg_conform/quality_metrics/configuration.rb +4 -2
- data/lib/svg_conform/references/base_reference.rb +153 -0
- data/lib/svg_conform/references/reference_manifest.rb +1 -2
- data/lib/svg_conform/report_comparator.rb +4 -4
- data/lib/svg_conform/requirements/base_requirement.rb +6 -0
- data/lib/svg_conform/requirements/external_references_requirement.rb +234 -0
- data/lib/svg_conform/requirements/link_validation_requirement.rb +2 -0
- data/lib/svg_conform/requirements/processing_features_requirement.rb +175 -0
- data/lib/svg_conform/requirements/svg_1_1_document_requirement.rb +213 -0
- data/lib/svg_conform/requirements.rb +6 -0
- data/lib/svg_conform/sax_document.rb +8 -4
- data/lib/svg_conform/sax_validation_handler.rb +22 -1
- data/lib/svg_conform/validation/css2_core_grammar.rb +359 -0
- data/lib/svg_conform/validation/css_references.rb +122 -0
- data/lib/svg_conform/validation/error_tracker.rb +40 -9
- data/lib/svg_conform/validation/svg_1_1_document_conformance.rb +154 -0
- data/lib/svg_conform/validation_context.rb +25 -5
- data/lib/svg_conform/validation_result.rb +91 -16
- data/lib/svg_conform/version.rb +1 -1
- data/lib/svg_conform/yaml_io.rb +20 -0
- data/lib/svg_conform.rb +25 -0
- data/spec/fixtures/diagnostics/external-reference-info.svg +6 -0
- data/spec/fixtures/plantuml/README.adoc +11 -0
- data/spec/fixtures/plantuml/unit-type.svg +1 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/animation.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/css-grammar.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/dtd-attribute.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/dtd-content-model.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/interaction.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/malformed-xml.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/script.svg +5 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/wrong-namespace.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/wrong-version.svg +4 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/invalid/xml-stylesheet.svg +5 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/valid/css.svg +5 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/valid/foreign-metadata.svg +6 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/valid/minimal-full.svg +5 -0
- data/spec/fixtures/profiles/svg_1_1_full_static/valid/xml-stylesheet.svg +5 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/animation.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/css-import.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/css-url.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-bad-png.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-empty-png.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-malformed-base64.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-malformed-percent.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-unsupported-type.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/file-like-fragment.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/http.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/https.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/interaction.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/protocol-relative.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/relative-file-fragment.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/relative-file.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/script.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/svg-data-excessive-nesting.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/svg-data-malformed-xml.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/svg-data-nested-external.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/urn.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/invalid/xml-stylesheet.svg +5 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/jpeg-base64.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/jpeg-percent.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/png-base64.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/png-percent.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/same-document-fragment.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/svg-base64.svg +4 -0
- data/spec/fixtures/profiles/svg_2_secure_static/valid/svg-percent.svg +4 -0
- data/spec/fixtures/quality_metrics/aliases.yml +7 -0
- data/spec/svg_conform/commands/check_command_spec.rb +22 -2
- data/spec/svg_conform/commands/profiles_command_spec.rb +18 -0
- data/spec/svg_conform/conformance_report_spec.rb +90 -0
- data/spec/svg_conform/diagnostics/lens_core_adapter_spec.rb +136 -0
- data/spec/svg_conform/gemspec_spec.rb +34 -0
- data/spec/svg_conform/profiles/metanorma_profile_spec.rb +48 -0
- data/spec/svg_conform/profiles/svg_1_1_full_static_profile_spec.rb +146 -0
- data/spec/svg_conform/profiles/svg_2_secure_static_profile_spec.rb +150 -0
- data/spec/svg_conform/profiles_plantuml_spec.rb +81 -0
- data/spec/svg_conform/quality_metrics/configuration_spec.rb +82 -0
- data/spec/svg_conform/references/data_uri_reference_spec.rb +97 -0
- data/spec/svg_conform/references/integration_spec.rb +30 -0
- data/spec/svg_conform/references/reference_classifier_spec.rb +17 -0
- data/spec/svg_conform/requirements/external_references_requirement_spec.rb +109 -0
- data/spec/svg_conform/requirements/link_validation_requirement_spec.rb +16 -0
- data/spec/svg_conform/requirements/processing_features_requirement_spec.rb +75 -0
- data/spec/svg_conform/requirements/svg_1_1_document_requirement_spec.rb +141 -0
- data/spec/svg_conform/requirements/text_as_path_requirement_spec.rb +9 -5
- data/spec/svg_conform/validation/css2_core_grammar_spec.rb +116 -0
- data/spec/svg_conform/validation/svg_1_1_document_conformance_spec.rb +144 -0
- data/spec/svg_conform/validation_context_source_plumbing_spec.rb +122 -0
- data/spec/svg_conform/validation_context_spec.rb +39 -0
- data/spec/svg_conform/validation_result_spec.rb +123 -0
- data/spec/svg_conform/yaml_io_spec.rb +41 -0
- data/svg_conform.gemspec +4 -2
- metadata +120 -6
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module SvgConform
|
|
6
|
+
module Validation
|
|
7
|
+
# Validates an SVG 1.1 document against the vendored SVG 1.1 flat DTD,
|
|
8
|
+
# following the validity procedure of SVG 1.1 Appendix G.2
|
|
9
|
+
# (conform.html#ConformingSVGDocuments).
|
|
10
|
+
#
|
|
11
|
+
# The DTD is resolved from the local data/svg_1_1 directory only. The
|
|
12
|
+
# caller's source is never modified: the Appendix G.2 transform is built
|
|
13
|
+
# from a separate parse of it, and a document that declares an external
|
|
14
|
+
# entity is rejected before any entity is expanded.
|
|
15
|
+
#
|
|
16
|
+
# Library choice (see the validator spec for the probe results): the
|
|
17
|
+
# bundled Moxml/Leptris stack parses the flat DTD but does not apply its
|
|
18
|
+
# conditional sections, so it accepts every document. Nokogiri (libxml2)
|
|
19
|
+
# validates the flat DTD offline.
|
|
20
|
+
class Svg11DocumentConformance
|
|
21
|
+
SVG_NS = "http://www.w3.org/2000/svg"
|
|
22
|
+
XLINK_NS = "http://www.w3.org/1999/xlink"
|
|
23
|
+
XML_NS = "http://www.w3.org/XML/1998/namespace"
|
|
24
|
+
DATA_DIR = File.expand_path("../../../data/svg_1_1", __dir__)
|
|
25
|
+
DTD_PATH = File.join(DATA_DIR, "svg11-flat-20110816.dtd")
|
|
26
|
+
MANIFEST_PATH = File.join(DATA_DIR, "manifest.yml")
|
|
27
|
+
EXTERNAL_ENTITY = /<!ENTITY\s+(?:%\s+)?[^\s>]+\s+(?:SYSTEM|PUBLIC)\b/m
|
|
28
|
+
|
|
29
|
+
Issue = Struct.new(:kind, :message, :line, keyword_init: true)
|
|
30
|
+
|
|
31
|
+
# Outcome of one validation. +transformed+ is the Appendix G.2 copy
|
|
32
|
+
# that was validated (nil when no copy could be built).
|
|
33
|
+
Result = Struct.new(:issues, :transformed, keyword_init: true) do
|
|
34
|
+
def valid?
|
|
35
|
+
issues.empty?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize(dtd_path: DTD_PATH)
|
|
40
|
+
@dtd_path = dtd_path
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate(source)
|
|
44
|
+
source = source.to_s
|
|
45
|
+
return failure(:external_entity, "external entities are not permitted") if external_entity?(source)
|
|
46
|
+
|
|
47
|
+
parsed = parse_expanded(source)
|
|
48
|
+
return parsed if parsed.is_a?(Result)
|
|
49
|
+
|
|
50
|
+
copy = transform(parsed)
|
|
51
|
+
return failure(:no_svg_root, "document element is not an SVG element") unless copy
|
|
52
|
+
|
|
53
|
+
Result.new(issues: dtd_issues(copy), transformed: copy)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Appendix G.2: drop foreign subtrees and attributes, drop prefixes,
|
|
57
|
+
# keep xlink attributes under the xlink prefix, and set the two
|
|
58
|
+
# namespace declarations on the document element. Returns the serialized
|
|
59
|
+
# copy (with the DOCTYPE for the local DTD) or nil without an SVG root.
|
|
60
|
+
def transform(doc)
|
|
61
|
+
root = doc.root
|
|
62
|
+
return unless root&.namespace&.href == SVG_NS
|
|
63
|
+
|
|
64
|
+
body = +""
|
|
65
|
+
write_element(root, body, root: true)
|
|
66
|
+
%(<?xml version="#{doc.version || '1.0'}" encoding="UTF-8"?>\n) +
|
|
67
|
+
%(<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "#{dtd_uri}">\n#{body}\n)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# A file URI that libxml2 accepts for POSIX paths and for Windows drive
|
|
73
|
+
# paths ("D:\\a\\x" becomes "file:///D:/a/x").
|
|
74
|
+
def dtd_uri
|
|
75
|
+
path = @dtd_path.tr("\\", "/")
|
|
76
|
+
path = "/#{path}" unless path.start_with?("/")
|
|
77
|
+
escaped = path.gsub(%r{[^A-Za-z0-9\-._~/:]}) { |char| char.bytes.map { |b| format("%%%02X", b) }.join }
|
|
78
|
+
"file://#{escaped}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def failure(kind, message, line = nil)
|
|
82
|
+
Result.new(issues: [Issue.new(kind: kind, message: message, line: line)], transformed: nil)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def external_entity?(source)
|
|
86
|
+
source.match?(EXTERNAL_ENTITY)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Entities are expanded (Appendix G.2) only after the source is known
|
|
90
|
+
# to declare no external entity, so expansion cannot read a file.
|
|
91
|
+
def parse_expanded(source)
|
|
92
|
+
Nokogiri::XML::Document.parse(source) { |config| config.strict.noent.nonet }
|
|
93
|
+
rescue Nokogiri::XML::SyntaxError => e
|
|
94
|
+
failure(:malformed_xml, e.message, e.line)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def write_element(node, out, root: false)
|
|
98
|
+
out << "<" << node.name
|
|
99
|
+
if root
|
|
100
|
+
out << %( xmlns="#{SVG_NS}" xmlns:xlink="#{XLINK_NS}")
|
|
101
|
+
end
|
|
102
|
+
node.attribute_nodes.each { |attr| write_attribute(attr, out) }
|
|
103
|
+
children = node.children
|
|
104
|
+
if children.empty?
|
|
105
|
+
out << "/>"
|
|
106
|
+
else
|
|
107
|
+
out << ">"
|
|
108
|
+
children.each { |child| write_node(child, out) }
|
|
109
|
+
out << "</" << node.name << ">"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def write_attribute(attr, out)
|
|
114
|
+
href = attr.namespace&.href
|
|
115
|
+
name = case href
|
|
116
|
+
when nil then attr.name
|
|
117
|
+
when XLINK_NS then "xlink:#{attr.name}"
|
|
118
|
+
when XML_NS then "xml:#{attr.name}"
|
|
119
|
+
end
|
|
120
|
+
out << " " << name << %(="#{escape_attribute(attr.value)}") if name
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def write_node(node, out)
|
|
124
|
+
case node.type
|
|
125
|
+
when Nokogiri::XML::Node::ELEMENT_NODE
|
|
126
|
+
write_element(node, out) if node.namespace&.href == SVG_NS
|
|
127
|
+
when Nokogiri::XML::Node::TEXT_NODE, Nokogiri::XML::Node::CDATA_SECTION_NODE
|
|
128
|
+
out << escape_text(node.content)
|
|
129
|
+
when Nokogiri::XML::Node::COMMENT_NODE
|
|
130
|
+
out << "<!--" << node.content << "-->"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def escape_text(text)
|
|
135
|
+
text.gsub("&", "&").gsub("<", "<").gsub(">", ">")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def escape_attribute(text)
|
|
139
|
+
escape_text(text).gsub('"', """).gsub("\t", "	")
|
|
140
|
+
.gsub("\n", " ").gsub("\r", " ")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def dtd_issues(copy)
|
|
144
|
+
doc = Nokogiri::XML::Document.parse(copy) do |config|
|
|
145
|
+
config.dtdload.dtdvalid.nonet
|
|
146
|
+
end
|
|
147
|
+
doc.errors.map do |error|
|
|
148
|
+
Issue.new(kind: :dtd, message: error.message.sub(/\A\d+:\d+: ERROR: /, ""),
|
|
149
|
+
line: error.line)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -100,6 +100,12 @@ module SvgConform
|
|
|
100
100
|
# The document being validated (nil in SAX mode)
|
|
101
101
|
attr_reader :document
|
|
102
102
|
|
|
103
|
+
# Original input text (nil when only a parsed tree was available)
|
|
104
|
+
attr_reader :source
|
|
105
|
+
|
|
106
|
+
# File path the source was read from, when known
|
|
107
|
+
attr_reader :source_path
|
|
108
|
+
|
|
103
109
|
# The profile containing requirements to validate against
|
|
104
110
|
attr_reader :profile
|
|
105
111
|
|
|
@@ -126,9 +132,13 @@ module SvgConform
|
|
|
126
132
|
@error_tracker.infos
|
|
127
133
|
end
|
|
128
134
|
|
|
129
|
-
def initialize(document, profile
|
|
135
|
+
def initialize(document, profile, source: nil, source_path: nil,
|
|
136
|
+
source_serialized: false)
|
|
130
137
|
@document = document
|
|
131
138
|
@profile = profile
|
|
139
|
+
@source = source
|
|
140
|
+
@source_path = source_path
|
|
141
|
+
@source_serialized = source_serialized
|
|
132
142
|
|
|
133
143
|
# Use TrackerFactory to create all tracker instances
|
|
134
144
|
trackers = Validation::TrackerFactory.create_all_trackers(document)
|
|
@@ -143,6 +153,12 @@ module SvgConform
|
|
|
143
153
|
|
|
144
154
|
# Get or create state for a requirement
|
|
145
155
|
# Each requirement gets its own state instance, preventing state pollution
|
|
156
|
+
# True when #source was re-serialized from a parsed tree, so original
|
|
157
|
+
# declaration/doctype/processing instructions cannot be proved.
|
|
158
|
+
def source_serialized?
|
|
159
|
+
@source_serialized ? true : false
|
|
160
|
+
end
|
|
161
|
+
|
|
146
162
|
def state_for(requirement)
|
|
147
163
|
@state_registry[requirement.class] ||= requirement.class::State.new
|
|
148
164
|
end
|
|
@@ -178,9 +194,11 @@ requirement_id: nil, severity: nil, fix: nil, data: {})
|
|
|
178
194
|
end
|
|
179
195
|
|
|
180
196
|
# Add an informational notice (delegates to ErrorTracker)
|
|
181
|
-
def add_notice(rule:, node:, message:, fix: nil, data: {}
|
|
197
|
+
def add_notice(rule:, node:, message:, fix: nil, data: {},
|
|
198
|
+
requirement_id: nil)
|
|
182
199
|
@error_tracker.add_notice(rule: rule, node: node, message: message,
|
|
183
|
-
fix: fix, data: data
|
|
200
|
+
fix: fix, data: data,
|
|
201
|
+
requirement_id: requirement_id)
|
|
184
202
|
end
|
|
185
203
|
|
|
186
204
|
def add_fix(fix)
|
|
@@ -229,12 +247,14 @@ column_number: nil)
|
|
|
229
247
|
end
|
|
230
248
|
|
|
231
249
|
# Add notice for external references (not errors)
|
|
232
|
-
def add_external_reference_notice(node:, reference:, message: nil
|
|
250
|
+
def add_external_reference_notice(node:, reference:, message: nil,
|
|
251
|
+
requirement_id: nil)
|
|
233
252
|
@error_tracker.add_notice(
|
|
234
253
|
rule: nil,
|
|
235
254
|
node: node,
|
|
236
255
|
message: message || "External reference: #{reference.value}",
|
|
237
|
-
data: { reference: reference },
|
|
256
|
+
data: { reference: reference.to_h },
|
|
257
|
+
requirement_id: requirement_id,
|
|
238
258
|
)
|
|
239
259
|
end
|
|
240
260
|
|
|
@@ -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, :reference_manifest
|
|
9
|
+
:infos, :fixes_applied, :fixed_document, :reference_manifest
|
|
10
10
|
|
|
11
11
|
def initialize(document, profile, context)
|
|
12
12
|
@document = document
|
|
@@ -14,6 +14,7 @@ module SvgConform
|
|
|
14
14
|
@errors = context.errors
|
|
15
15
|
@warnings = context.warnings
|
|
16
16
|
@validity_errors = context.validity_errors
|
|
17
|
+
@infos = context.infos
|
|
17
18
|
@fixes_applied = []
|
|
18
19
|
@fixed_document = nil
|
|
19
20
|
@reference_manifest = context.reference_manifest
|
|
@@ -23,8 +24,9 @@ module SvgConform
|
|
|
23
24
|
@errors.empty? && @validity_errors.empty?
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
# True for errors and validity errors, matching valid?.
|
|
26
28
|
def has_errors?
|
|
27
|
-
|
|
29
|
+
error_count.positive?
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def has_warnings?
|
|
@@ -39,14 +41,19 @@ module SvgConform
|
|
|
39
41
|
!@fixed_document.nil?
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
# Errors plus validity errors: both are "error" in the canonical report.
|
|
42
45
|
def error_count
|
|
43
|
-
@errors.size
|
|
46
|
+
@errors.size + @validity_errors.size
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def warning_count
|
|
47
50
|
@warnings.size
|
|
48
51
|
end
|
|
49
52
|
|
|
53
|
+
def info_count
|
|
54
|
+
@infos.size
|
|
55
|
+
end
|
|
56
|
+
|
|
50
57
|
def issue_count
|
|
51
58
|
error_count + warning_count
|
|
52
59
|
end
|
|
@@ -114,21 +121,46 @@ module SvgConform
|
|
|
114
121
|
end
|
|
115
122
|
end
|
|
116
123
|
|
|
124
|
+
# The canonical LensCore::Report for this result (schema version 1).
|
|
125
|
+
# The timestamp is only included when the caller supplies one.
|
|
126
|
+
# Raises when the optional lens_core gem is not installed.
|
|
127
|
+
def to_lens_core_report(timestamp: nil)
|
|
128
|
+
unless SvgConform.lens_core_available?
|
|
129
|
+
raise SvgConform::Error,
|
|
130
|
+
"lens_core is not installed; the canonical diagnostics " \
|
|
131
|
+
"report requires it"
|
|
132
|
+
end
|
|
133
|
+
Diagnostics::LensCoreAdapter.call(self, timestamp: timestamp)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Report as a symbol-keyed Hash: the canonical LensCore shape (with
|
|
137
|
+
# the compatibility extensions +file+, +fixes_applied+, +fixable+ and
|
|
138
|
+
# +reference_manifest+) when lens_core is installed, else the plain
|
|
139
|
+
# structural shape.
|
|
117
140
|
def to_h
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
warnings: @warnings.map(&:to_h),
|
|
124
|
-
fixes_applied: @fixes_applied.size,
|
|
125
|
-
fixable: fixable_count,
|
|
126
|
-
reference_manifest: @reference_manifest.to_h,
|
|
127
|
-
}
|
|
141
|
+
if SvgConform.lens_core_available?
|
|
142
|
+
JSON.parse(to_json, symbolize_names: true)
|
|
143
|
+
else
|
|
144
|
+
structural_h
|
|
145
|
+
end
|
|
128
146
|
end
|
|
129
147
|
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
# Canonical LensCore JSON when lens_core is installed, else the plain
|
|
149
|
+
# structural serialization.
|
|
150
|
+
def to_json(*)
|
|
151
|
+
if SvgConform.lens_core_available?
|
|
152
|
+
LensCore::Renderers::Json.render(compatibility_report).chomp
|
|
153
|
+
else
|
|
154
|
+
JSON.pretty_generate(structural_h)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def to_yaml(*)
|
|
159
|
+
if SvgConform.lens_core_available?
|
|
160
|
+
LensCore::Renderers::Yaml.render(compatibility_report)
|
|
161
|
+
else
|
|
162
|
+
structural_h.to_yaml
|
|
163
|
+
end
|
|
132
164
|
end
|
|
133
165
|
|
|
134
166
|
# Export manifest separately for detailed analysis
|
|
@@ -147,7 +179,33 @@ module SvgConform
|
|
|
147
179
|
|
|
148
180
|
private
|
|
149
181
|
|
|
182
|
+
def compatibility_report(timestamp: nil)
|
|
183
|
+
report = to_lens_core_report(timestamp: timestamp)
|
|
184
|
+
extensions = report.extensions.merge(
|
|
185
|
+
"fixes_applied" => @fixes_applied.size,
|
|
186
|
+
"fixable" => fixable_count,
|
|
187
|
+
"reference_manifest" => Diagnostics::LensCoreAdapter.json_value(
|
|
188
|
+
@reference_manifest.to_h, "reference_manifest"
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
LensCore::Report.new(
|
|
192
|
+
producer: report.producer, profile: report.profile,
|
|
193
|
+
valid: report.valid, diagnostics: report.diagnostics,
|
|
194
|
+
timestamp: report.timestamp, extensions: extensions
|
|
195
|
+
)
|
|
196
|
+
end
|
|
197
|
+
|
|
150
198
|
def to_text
|
|
199
|
+
if SvgConform.lens_core_available?
|
|
200
|
+
lines = [LensCore::Renderers::Human.render(to_lens_core_report).chomp]
|
|
201
|
+
if fixable?
|
|
202
|
+
lines << ""
|
|
203
|
+
lines << "Fixes Available: #{fixable_count}"
|
|
204
|
+
lines << "Fixes Applied: #{@fixes_applied.size}" if fixed?
|
|
205
|
+
end
|
|
206
|
+
return lines.join("\n")
|
|
207
|
+
end
|
|
208
|
+
|
|
151
209
|
lines = []
|
|
152
210
|
lines << "SVG Conformance Report"
|
|
153
211
|
lines << ("=" * 22)
|
|
@@ -179,9 +237,26 @@ module SvgConform
|
|
|
179
237
|
lines << ""
|
|
180
238
|
end
|
|
181
239
|
|
|
182
|
-
|
|
240
|
+
if valid? && !has_warnings?
|
|
241
|
+
lines << "✓ Document is valid and conforms to the #{@profile.name} profile."
|
|
242
|
+
end
|
|
183
243
|
|
|
184
244
|
lines.join("\n")
|
|
185
245
|
end
|
|
246
|
+
|
|
247
|
+
# The plain structural shape (the 0.2.4 serialization), used when the
|
|
248
|
+
# optional lens_core renderer is not installed.
|
|
249
|
+
def structural_h
|
|
250
|
+
{
|
|
251
|
+
file: @document&.file_path,
|
|
252
|
+
profile: @profile.name,
|
|
253
|
+
valid: valid?,
|
|
254
|
+
errors: @errors.map(&:to_h),
|
|
255
|
+
warnings: @warnings.map(&:to_h),
|
|
256
|
+
fixes_applied: @fixes_applied.size,
|
|
257
|
+
fixable: fixable_count,
|
|
258
|
+
reference_manifest: @reference_manifest.to_h,
|
|
259
|
+
}
|
|
260
|
+
end
|
|
186
261
|
end
|
|
187
262
|
end
|
data/lib/svg_conform/version.rb
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yeptris/psych"
|
|
4
|
+
|
|
5
|
+
module SvgConform
|
|
6
|
+
# YAML operations through Yeptris without installing its global drop-in.
|
|
7
|
+
module YamlIO
|
|
8
|
+
def self.load_file(...)
|
|
9
|
+
Yeptris::Psych.load_file(...)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.safe_load_file(...)
|
|
13
|
+
Yeptris::Psych.safe_load_file(...)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.dump(...)
|
|
17
|
+
Yeptris::Psych.dump(...)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/svg_conform.rb
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
4
|
|
|
5
|
+
# lens_core is optional (0.2.5 shipped it as a hard dependency while the
|
|
6
|
+
# gem is still unpublished, which made every fresh resolve unsolvable).
|
|
7
|
+
# When present it renders the canonical diagnostics reports; when absent
|
|
8
|
+
# the plain structural serializers answer instead.
|
|
9
|
+
begin
|
|
10
|
+
require "lens_core"
|
|
11
|
+
rescue LoadError
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
|
|
5
15
|
# Configure Lutaml::Model adapters
|
|
6
16
|
Lutaml::Model::Config.configure do |config|
|
|
7
17
|
config.xml_adapter_type = :leptris
|
|
@@ -39,7 +49,22 @@ module SvgConform
|
|
|
39
49
|
class ProfileError < Error; end
|
|
40
50
|
class RuleError < Error; end
|
|
41
51
|
|
|
52
|
+
# True when the optional lens_core diagnostics renderer is installed.
|
|
53
|
+
def self.lens_core_available?
|
|
54
|
+
return @lens_core_available unless @lens_core_available.nil?
|
|
55
|
+
|
|
56
|
+
@lens_core_available = begin
|
|
57
|
+
require "lens_core"
|
|
58
|
+
true
|
|
59
|
+
rescue LoadError
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
require_relative "svg_conform/diagnostics/lens_core_adapter"
|
|
65
|
+
|
|
42
66
|
# Autoload core classes
|
|
67
|
+
autoload :YamlIO, File.expand_path("svg_conform/yaml_io", __dir__)
|
|
43
68
|
autoload :Validator, File.expand_path("svg_conform/validator", __dir__)
|
|
44
69
|
autoload :Fixer, "svg_conform/fixer"
|
|
45
70
|
autoload :RemediationEngine, "svg_conform/remediation_engine"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny" viewBox="0 0 100 100">
|
|
3
|
+
<a xlink:href="https://example.com/page">
|
|
4
|
+
<rect x="10" y="10" width="50" height="50"/>
|
|
5
|
+
</a>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
= PlantUML SVG regression fixture
|
|
2
|
+
|
|
3
|
+
`unit-type.svg` is native output from PlantUML 1.2026.3 (bundled with metanorma-plugin-plantuml 1.0.15) using the Smetana layout engine.
|
|
4
|
+
It represents the UnitsML XML Schema `UnitType` content model used by
|
|
5
|
+
https://github.com/unitsml/docs/pull/9[UnitsML documentation PR 9].
|
|
6
|
+
The embedded PlantUML source records its schema labels and diagram input.
|
|
7
|
+
|
|
8
|
+
The diagram links target anchors in the surrounding documentation page.
|
|
9
|
+
Standalone profile tests therefore check attribute validation separately from
|
|
10
|
+
those document-level references; the UnitsML documentation checks the targets.
|
|
11
|
+
The test preserves the SVG's measured text layout and link titles.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="CLASS" height="482px" preserveAspectRatio="none" style="width:764px;height:482px;background:#FFFFFF;" version="1.1" viewBox="0 0 764 482" width="764px" zoomAndPan="magnify"><?plantuml 1.2026.3?><defs/><g><!--class schema_UnitType--><g class="entity" data-qualified-name="schema_UnitType" data-source-line="7" id="ent0002"><a href="#xsd_UnitType" target="_top" title="#xsd_UnitType" xlink:actuate="onRequest" xlink:href="#xsd_UnitType" xlink:show="new" xlink:title="#xsd_UnitType" xlink:type="simple"><rect fill="#F1F1F1" height="83.1875" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="261.0264" x="6" y="202.9611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="62.8359" x="105.0952" y="220.9562">UnitType</text><line style="stroke:#181818;stroke-width:0.5;" x1="7" x2="266.0264" y1="229.258" y2="229.258"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81.5869" x="12" y="246.2531">@xml:id [1]</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="242.2588" x="12" y="262.55">@timeStamp : xsd:dateTime [0..1]</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="249.0264" x="12" y="278.8469">@dimensionURL : xsd:anyURI [0..1]</text></a></g><!--class schema_UnitType_1--><g class="entity" data-qualified-name="schema_UnitType_1" data-source-line="15" id="ent0003"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75.0566" x="319" y="231.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="69.0566" x="322" y="249.4562">Sequence</text></g><!--class schema_UnitType_1_0--><g class="entity" data-qualified-name="schema_UnitType_1_0" data-source-line="17" id="ent0005"><a href="#xsd_UnitSystem" target="_top" title="#xsd_UnitSystem" xlink:actuate="onRequest" xlink:href="#xsd_UnitSystem" xlink:show="new" xlink:title="#xsd_UnitSystem" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="186.8652" x="526" y="11.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="180.8652" x="529" y="29.4562">UnitSystem : SystemType</text></a></g><!--class schema_UnitType_1_1--><g class="entity" data-qualified-name="schema_UnitType_1_1" data-source-line="19" id="ent0007"><a href="#xsd_UnitName" target="_top" title="#xsd_UnitName" xlink:actuate="onRequest" xlink:href="#xsd_UnitName" xlink:show="new" xlink:title="#xsd_UnitName" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="165.0586" x="537" y="55.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="159.0586" x="540" y="73.4562">UnitName : NameType</text></a></g><!--class schema_UnitType_1_2--><g class="entity" data-qualified-name="schema_UnitType_1_2" data-source-line="21" id="ent0009"><a href="#xsd_UnitSymbol" target="_top" title="#xsd_UnitSymbol" xlink:actuate="onRequest" xlink:href="#xsd_UnitSymbol" xlink:show="new" xlink:title="#xsd_UnitSymbol" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="186.7559" x="526" y="99.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="180.7559" x="529" y="117.4562">UnitSymbol : SymbolType</text></a></g><!--class schema_UnitType_1_3--><g class="entity" data-qualified-name="schema_UnitType_1_3" data-source-line="23" id="ent0011"><a href="#xsd_CodeListValue" target="_top" title="#xsd_CodeListValue" xlink:actuate="onRequest" xlink:href="#xsd_CodeListValue" xlink:show="new" xlink:title="#xsd_CodeListValue" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="253.6455" x="492.5" y="143.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="247.6455" x="495.5" y="161.4562">CodeListValue : CodeListValueType</text></a></g><!--class schema_UnitType_1_4--><g class="entity" data-qualified-name="schema_UnitType_1_4" data-source-line="25" id="ent0013"><a href="#xsd_RootUnits" target="_top" title="#xsd_RootUnits" xlink:actuate="onRequest" xlink:href="#xsd_RootUnits" xlink:show="new" xlink:title="#xsd_RootUnits" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="190.2354" x="524.5" y="187.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="184.2354" x="527.5" y="205.4562">RootUnits : RootUnitsType</text></a></g><!--class schema_UnitType_1_5--><g class="entity" data-qualified-name="schema_UnitType_1_5" data-source-line="27" id="ent0015"><a href="#xsd_Conversions" target="_top" title="#xsd_Conversions" xlink:actuate="onRequest" xlink:href="#xsd_Conversions" xlink:show="new" xlink:title="#xsd_Conversions" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="225.5225" x="506.5" y="231.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="219.5225" x="509.5" y="249.4562">Conversions : ConversionsType</text></a></g><!--class schema_UnitType_1_6--><g class="entity" data-qualified-name="schema_UnitType_1_6" data-source-line="29" id="ent0017"><a href="#xsd_QuantityReference" target="_top" title="#xsd_QuantityReference" xlink:actuate="onRequest" xlink:href="#xsd_QuantityReference" xlink:show="new" xlink:title="#xsd_QuantityReference" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="257.3232" x="491" y="275.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="251.3232" x="494" y="293.4562">QuantityReference : ReferenceType</text></a></g><!--class schema_UnitType_1_7--><g class="entity" data-qualified-name="schema_UnitType_1_7" data-source-line="31" id="ent0019"><a href="#xsd_UnitVersionHistory" target="_top" title="#xsd_UnitVersionHistory" xlink:actuate="onRequest" xlink:href="#xsd_UnitVersionHistory" xlink:show="new" xlink:title="#xsd_UnitVersionHistory" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="217.9688" x="510.5" y="319.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="211.9688" x="513.5" y="337.4562">UnitVersionHistory : NoteType</text></a></g><!--class schema_UnitType_1_8--><g class="entity" data-qualified-name="schema_UnitType_1_8" data-source-line="33" id="ent0021"><a href="#xsd_UnitDefinition" target="_top" title="#xsd_UnitDefinition" xlink:actuate="onRequest" xlink:href="#xsd_UnitDefinition" xlink:show="new" xlink:title="#xsd_UnitDefinition" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="218.0371" x="510.5" y="363.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="212.0371" x="513.5" y="381.4562">UnitDefinition : DefinitionType</text></a></g><!--class schema_UnitType_1_9--><g class="entity" data-qualified-name="schema_UnitType_1_9" data-source-line="35" id="ent0023"><a href="#xsd_UnitHistory" target="_top" title="#xsd_UnitHistory" xlink:actuate="onRequest" xlink:href="#xsd_UnitHistory" xlink:show="new" xlink:title="#xsd_UnitHistory" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="182.0664" x="528.5" y="407.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="176.0664" x="531.5" y="425.4562">UnitHistory : HistoryType</text></a></g><!--class schema_UnitType_1_10--><g class="entity" data-qualified-name="schema_UnitType_1_10" data-source-line="37" id="ent0025"><a href="#xsd_UnitRemark" target="_top" title="#xsd_UnitRemark" xlink:actuate="onRequest" xlink:href="#xsd_UnitRemark" xlink:show="new" xlink:title="#xsd_UnitRemark" xlink:type="simple"><rect fill="#F1F1F1" height="26.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="191.2949" x="524" y="451.4611"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="185.2949" x="527" y="469.4562">UnitRemark : RemarkType</text></a></g><g class="link" data-entity-1="ent0002" data-entity-2="ent0003" data-link-type="dependency" id="lnk4"><polygon fill="#181818" points="318.7283,244.4611,309.7283,240.4611,313.7283,244.4611,309.7283,248.4611,318.7283,244.4611" style="stroke:#181818;stroke-width:1;"/><path d="M267.1453,244.4611 C285.9807,244.4611 297.9624,244.4611 312.7283,244.4611" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="12.4033" x="287" y="239.528">1.</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0005" data-link-type="dependency" id="lnk6"><polygon fill="#181818" points="525.9883,12.6966,516.9838,8.7068,520.9883,12.7022,516.9929,16.7068,525.9883,12.6966" style="stroke:#181818;stroke-width:1;"/><path d="M359.1852,231.3775 C364.636,191.2235 383.1284,70.9857 412,46.4611 C442.9818,20.1441 480.7295,12.748 519.9883,12.7034" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="13.528">1. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0007" data-link-type="dependency" id="lnk8"><polygon fill="#181818" points="536.7861,61.2113,527.7282,57.3442,531.7867,61.2849,527.846,65.3433,536.7861,61.2113" style="stroke:#181818;stroke-width:1;"/><path d="M358.0947,231.3355 C360.1818,202.1144 369.5909,131.5906 412,97.4611 C446.5495,69.6568 489.3185,61.9101 530.7868,61.2996" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="64.528">2. [1..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0009" data-link-type="dependency" id="lnk10"><polygon fill="#181818" points="525.7469,109.9772,516.4492,106.7288,520.7638,110.3873,517.1053,114.7018,525.7469,109.9772" style="stroke:#181818;stroke-width:1;"/><path d="M360.7034,231.3623 C366.7432,209.6648 381.9915,166.5696 412,145.4611 C444.7387,122.4322 481.5987,113.6102 519.7671,110.4693" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="115.528">3. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0011" data-link-type="dependency" id="lnk12"><polygon fill="#181818" points="492.4722,163.7255,482.8983,161.4147,487.5542,164.6275,484.3415,169.2835,492.4722,163.7255" style="stroke:#181818;stroke-width:1;"/><path d="M366.6717,231.2022 C376.3599,218.3116 392.8653,199.2826 412,189.4611 C436.6282,176.8199 459.0035,169.8639 486.5706,164.8079" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="163.528">4. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0013" data-link-type="dependency" id="lnk14"><polygon fill="#181818" points="524.2499,204.9716,514.933,201.7787,519.2693,205.4114,515.6366,209.7477,524.2499,204.9716" style="stroke:#181818;stroke-width:1;"/><path d="M385.571,231.4003 C393.9182,227.9929 403.1897,224.663 412,222.4611 C448.1146,213.4354 482.7117,208.6393 518.2732,205.4994" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="51.4858" x="417" y="207.528">5. [0..1]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0015" data-link-type="dependency" id="lnk16"><polygon fill="#181818" points="506.2441,244.4611,497.2441,240.4611,501.2441,244.4611,497.2441,248.4611,506.2441,244.4611" style="stroke:#181818;stroke-width:1;"/><path d="M394.0658,244.4611 C423.3172,244.4611 459.9634,244.4611 500.2441,244.4611" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="51.4858" x="417" y="239.528">6. [0..1]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0017" data-link-type="dependency" id="lnk18"><polygon fill="#181818" points="490.7725,281.4718,482.3043,276.4428,485.807,280.8848,481.3651,284.3875,490.7725,281.4718" style="stroke:#181818;stroke-width:1;"/><path d="M384.1161,257.5622 C392.8149,261.3333 402.6473,265.0695 412,267.4611 C437.1368,273.8888 458.5102,277.6576 484.814,280.7674" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="262.528">7. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0019" data-link-type="dependency" id="lnk20"><polygon fill="#181818" points="510.358,329.6396,501.9538,324.5045,505.4004,328.9902,500.9147,332.4367,510.358,329.6396" style="stroke:#181818;stroke-width:1;"/><path d="M366.1838,257.7426 C375.7157,271.1367 392.3102,291.2501 412,301.4611 C441.9295,316.9824 471.4166,324.5383 504.4089,328.8603" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="296.528">8. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0021" data-link-type="dependency" id="lnk22"><polygon fill="#181818" points="510.4419,378.6862,501.9571,373.6854,505.4745,378.1157,501.0443,381.6332,510.4419,378.6862" style="stroke:#181818;stroke-width:1;"/><path d="M360.5057,257.4957 C366.3254,279.5507 381.3508,323.9261 412,345.4611 C440.4018,365.4171 470.4849,374.0971 504.4811,378.0016" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49.7148" x="418" y="340.528">9. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0023" data-link-type="dependency" id="lnk24"><polygon fill="#181818" points="528.3507,429.0662,519.4444,424.8618,523.352,428.9521,519.2617,432.8597,528.3507,429.0662" style="stroke:#181818;stroke-width:1;"/><path d="M357.9661,257.8906 C359.7899,287.7817 368.6949,359.8859 412,394.4611 C444.2615,420.219 482.858,428.0275 522.3523,428.9293" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57.9858" x="414" y="389.528">10. [0..*]</text></g><g class="link" data-entity-1="ent0003" data-entity-2="ent0025" data-link-type="dependency" id="lnk26"><polygon fill="#181818" points="536.9087,477.4619,527.727,473.8988,531.9145,477.7021,528.1112,481.8895,536.9087,477.4619" style="stroke:#181818;stroke-width:1;"/><path d="M359.1013,257.472 C364.368,297.9913 382.5214,420.6877 412,445.4611 C445.9699,474.0089 489.102,479.7604 530.9157,477.7501" fill="none" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57.9858" x="414" y="440.528">11. [0..*]</text></g><?plantuml-src bPJBRjim44Nt_0ectHM8OVbjBu84xQ81WWALusmCmQ2biKr495MHQgCM-VSE9UjXMHKWRpIaxkYU6PDp4yNiA1WDMQeIGxK0moGZD-tR8HmCDOh6_7WoDE0n-8OhIKuy00eYCYaL8FOGwtpBvx4V0j5lN4OiPeBw8TFwqPYOxnC3JxwIQ_u7wB0cqISMgDzS7kc1XHf8ba9lDvARbpI2MyeqrVu91DiLUtIx_VIkWtBjUVGlUNWNuOe7TEjuvC5m0Ml3H4HN59MhW1bumJsw7Gu6LX3WGcgiPUC-dpLCfXltgL2G3q8AbZNyJ43whImxXpJfxkxkhrLeuWpgnQrJRK3WTXxyhzQTinlMwytLdaUk7LfzCTiwPsLzzke0tndsOqNjeyFSgPjRRGzhM_G8hPsczJeLUnLcjTkWmtvqMRjLzm0OeSMuMViN5S0pr-QLXObjmCMw0sLSe5mazA0PezcaIUCgPQn6uyioxg2O51Ibk0V114scW-A8b_sGl_06urtGMJVALGV5jEf5AU_1CKMZMPFZHyAauIPruG2nlMws8qNSmJ8hMAu-q8DeXcRpjblocbVt3Vzl5QVslYZJHJEl7zZBv1uuSpHQjE5yXGF71tuK5TMY0sXH1weoUi0iq6hP1bCrvHnrO2ph6FqRihHJRDW6uQ8oVilEYWswHjd58CtbVQPPDachKNh-_GC0?></g></svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="20" height="20">
|
|
3
|
+
<style type="text/css">rect.a { fill: red; stroke: #000 } @media print { rect { stroke-width: 2 } }</style>
|
|
4
|
+
<rect class="a" width="10" height="10" style="fill: blue; opacity: 0.5"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ex="http://example.com/ns" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="20" height="20" ex:note="kept out">
|
|
3
|
+
<metadata><ex:info><ex:item>not SVG</ex:item></ex:info></metadata>
|
|
4
|
+
<ex:extension><ex:deep/></ex:extension>
|
|
5
|
+
<a xlink:href="#r"><rect id="r" width="10" height="10"/></a>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="20" height="20" viewBox="0 0 20 20">
|
|
4
|
+
<rect x="2" y="2" width="16" height="16" fill="#336699"/>
|
|
5
|
+
</svg>
|