svg_conform 0.1.12 → 0.2.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +33 -109
  3. data/Gemfile +2 -0
  4. data/README.adoc +164 -0
  5. data/config/profiles/metanorma.yml +21 -14
  6. data/config/quality_metrics.yml +212 -0
  7. data/docs/image_quality.adoc +526 -0
  8. data/docs/performance.adoc +31 -0
  9. data/docs/profiles.adoc +33 -1
  10. data/docs/requirements.adoc +178 -0
  11. data/lib/svg_conform/cli.rb +5 -0
  12. data/lib/svg_conform/commands/quality.rb +249 -0
  13. data/lib/svg_conform/compatibility/report_formatter.rb +3 -5
  14. data/lib/svg_conform/compatibility_analyzer.rb +3 -3
  15. data/lib/svg_conform/document_analyzer.rb +9 -4
  16. data/lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb +5 -5
  17. data/lib/svg_conform/image_quality_analyzer.rb +225 -0
  18. data/lib/svg_conform/quality_metrics/complexity_calculator.rb +98 -0
  19. data/lib/svg_conform/quality_metrics/complexity_metrics.rb +56 -0
  20. data/lib/svg_conform/quality_metrics/configuration.rb +342 -0
  21. data/lib/svg_conform/quality_metrics/error_analyzer.rb +90 -0
  22. data/lib/svg_conform/quality_metrics/error_breakdown.rb +80 -0
  23. data/lib/svg_conform/quality_metrics/feature_detector.rb +55 -0
  24. data/lib/svg_conform/quality_metrics/feature_flags.rb +65 -0
  25. data/lib/svg_conform/quality_metrics/quality_calculator.rb +130 -0
  26. data/lib/svg_conform/quality_metrics/quality_report_formatter.rb +70 -0
  27. data/lib/svg_conform/quality_metrics/quality_result.rb +101 -0
  28. data/lib/svg_conform/quality_metrics/quality_score.rb +76 -0
  29. data/lib/svg_conform/quality_report.rb +342 -0
  30. data/lib/svg_conform/remediations/color_remediation.rb +1 -3
  31. data/lib/svg_conform/remediations/viewbox_remediation.rb +5 -9
  32. data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +1 -1
  33. data/lib/svg_conform/requirements/style_requirement.rb +3 -2
  34. data/lib/svg_conform/requirements/text_as_path_requirement.rb +98 -0
  35. data/lib/svg_conform/requirements.rb +2 -0
  36. data/lib/svg_conform/sax_validation_handler.rb +3 -3
  37. data/lib/svg_conform/semantic_comparator.rb +3 -3
  38. data/lib/svg_conform/validation_result.rb +1 -1
  39. data/lib/svg_conform/version.rb +1 -1
  40. data/lib/svg_conform.rb +23 -0
  41. data/lib/tasks/fixtures.rake +1 -1
  42. data/spec/svg_conform/profiles/lucid_profile_spec.rb +0 -2
  43. data/spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb +9 -9
  44. data/spec/svg_conform/requirements/allowed_elements_requirement_spec.rb +120 -1
  45. data/spec/svg_conform/requirements/color_restrictions_requirement_spec.rb +0 -1
  46. data/spec/svg_conform/requirements/font_family_requirement_spec.rb +0 -1
  47. data/spec/svg_conform/requirements/forbidden_content_requirement_spec.rb +0 -1
  48. data/spec/svg_conform/requirements/id_reference_requirement_spec.rb +0 -1
  49. data/spec/svg_conform/requirements/invalid_id_references_requirement_spec.rb +0 -1
  50. data/spec/svg_conform/requirements/link_validation_requirement_spec.rb +0 -1
  51. data/spec/svg_conform/requirements/namespace_attributes_requirement_spec.rb +0 -1
  52. data/spec/svg_conform/requirements/namespace_requirement_spec.rb +0 -1
  53. data/spec/svg_conform/requirements/no_external_css_requirement_spec.rb +0 -1
  54. data/spec/svg_conform/requirements/style_promotion_requirement_spec.rb +0 -1
  55. data/spec/svg_conform/requirements/style_requirement_spec.rb +0 -1
  56. data/spec/svg_conform/requirements/text_as_path_requirement_spec.rb +142 -0
  57. data/spec/svg_conform/requirements/viewbox_required_requirement_spec.rb +0 -1
  58. data/spec/svg_conform_spec.rb +2 -2
  59. data/spec/svgcheck_compatibility_spec.rb +5 -13
  60. data/svg_conform.gemspec +3 -1
  61. metadata +37 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 162ed406493cfabb9a3cb94a3a6ba2ee3ed4ae95d3edf912034c17974d5cc496
4
- data.tar.gz: 56cb8b2d5e94bcce3cc1e32b513a738ac2140f0e86848e6c101fc08ed16cfc7a
3
+ metadata.gz: 136c672fbb6005495a240d2d8b7f30000fae008cd16fff6bfe51a74e7dbe2262
4
+ data.tar.gz: 679452859b709b2e9ef78916918ea9e58040ebfb382291b8c6543b9062a5ee09
5
5
  SHA512:
6
- metadata.gz: 1bcbdf1a7dbc43f044aa875d7a310b85064bcd47d5f227cf962303d1f3a0d024fe926b87ffc201fbf5256b0e150cfa71b5a8702e84b062b37d7935a50bc96b5b
7
- data.tar.gz: 817c9ac5561b509c39e9f4d228b4165d3943a242bb83a88c77e8388bbce99c6746dc9c9df464a729ce44160c331b08ce9dfcc06e98c676c2944631eaf35346e5
6
+ metadata.gz: 26a32728202834b61e892d78ef87bb6bae0030e768414985cfe0bfa208da9fa21828035fe6f247225c6cd37a299dff6fe91863c70137c528d7e24a9aade3250e
7
+ data.tar.gz: 47b4ba1d348244438817269403a2b5b09019687024849e691ff6fe4dcbb9d4fed1a322f132758af38f8ba1310fb0666cc5d9dbaeef93192a8edaf19df66bb1c9
data/.rubocop_todo.yml CHANGED
@@ -1,17 +1,11 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-04-04 08:57:24 UTC using RuboCop version 1.86.0.
3
+ # on 2026-04-09 13:07:51 UTC using RuboCop version 1.86.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- # This cop supports safe autocorrection (--autocorrect).
11
- Gemspec/RequireMFA:
12
- Exclude:
13
- - 'svg_conform.gemspec'
14
-
15
9
  # Offense count: 1
16
10
  # This cop supports safe autocorrection (--autocorrect).
17
11
  # Configuration parameters: EnforcedStyleAlignWith.
@@ -20,44 +14,19 @@ Layout/BlockAlignment:
20
14
  Exclude:
21
15
  - 'spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb'
22
16
 
23
- # Offense count: 1
24
- # This cop supports safe autocorrection (--autocorrect).
25
- # Configuration parameters: EnforcedStyle.
26
- # SupportedStyles: leading, trailing
27
- Layout/LineContinuationLeadingSpace:
28
- Exclude:
29
- - 'spec/svgcheck_compatibility_spec.rb'
30
-
31
- # Offense count: 5
32
- # This cop supports safe autocorrection (--autocorrect).
33
- # Configuration parameters: EnforcedStyle, IndentationWidth.
34
- # SupportedStyles: aligned, indented
35
- Layout/LineEndStringConcatenationIndentation:
36
- Exclude:
37
- - 'lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb'
38
- - 'spec/svgcheck_compatibility_spec.rb'
39
-
40
- # Offense count: 649
17
+ # Offense count: 667
41
18
  # This cop supports safe autocorrection (--autocorrect).
42
19
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
43
20
  # URISchemes: http, https
44
21
  Layout/LineLength:
45
22
  Enabled: false
46
23
 
47
- # Offense count: 13
24
+ # Offense count: 9
48
25
  # This cop supports safe autocorrection (--autocorrect).
49
- Lint/AmbiguousOperatorPrecedence:
26
+ # Configuration parameters: AllowInHeredoc.
27
+ Layout/TrailingWhitespace:
50
28
  Exclude:
51
- - 'lib/svg_conform/compatibility_analyzer.rb'
52
- - 'lib/svg_conform/semantic_comparator.rb'
53
- - 'lib/svg_conform/validation_result.rb'
54
-
55
- # Offense count: 1
56
- # This cop supports unsafe autocorrection (--autocorrect-all).
57
- # Configuration parameters: RequireParenthesesForMethodChains.
58
- Lint/AmbiguousRange:
59
- Exclude:
60
- - 'lib/svg_conform/compatibility/report_formatter.rb'
29
+ - 'spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb'
61
30
 
62
31
  # Offense count: 3
63
32
  # Configuration parameters: AllowedMethods.
@@ -91,7 +60,14 @@ Lint/DuplicateMethods:
91
60
  - 'lib/svg_conform/sax_validation_handler.rb'
92
61
  - 'spec/svg_conform/validation_context_spec.rb'
93
62
 
94
- # Offense count: 2
63
+ # Offense count: 1
64
+ # This cop supports safe autocorrection (--autocorrect).
65
+ # Configuration parameters: AllowComments.
66
+ Lint/EmptyConditionalBody:
67
+ Exclude:
68
+ - 'spec/svgcheck_compatibility_spec.rb'
69
+
70
+ # Offense count: 1
95
71
  Lint/HashCompareByIdentity:
96
72
  Exclude:
97
73
  - 'lib/svg_conform/document_analyzer.rb'
@@ -104,24 +80,12 @@ Lint/MissingSuper:
104
80
  - 'lib/svg_conform/compatibility/validity_analysis.rb'
105
81
  - 'lib/svg_conform/sax_validation_handler.rb'
106
82
 
107
- # Offense count: 1
108
- # This cop supports unsafe autocorrection (--autocorrect-all).
109
- Lint/RedundantDirGlobSort:
110
- Exclude:
111
- - 'lib/tasks/fixtures.rake'
112
-
113
- # Offense count: 1
114
- # This cop supports unsafe autocorrection (--autocorrect-all).
115
- Lint/SuppressedExceptionInNumberConversion:
116
- Exclude:
117
- - 'lib/svg_conform/remediations/viewbox_remediation.rb'
118
-
119
- # Offense count: 145
83
+ # Offense count: 160
120
84
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
121
85
  Metrics/AbcSize:
122
86
  Enabled: false
123
87
 
124
- # Offense count: 24
88
+ # Offense count: 25
125
89
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
126
90
  # AllowedMethods: refine
127
91
  Metrics/BlockLength:
@@ -132,22 +96,22 @@ Metrics/BlockLength:
132
96
  Metrics/BlockNesting:
133
97
  Max: 4
134
98
 
135
- # Offense count: 125
99
+ # Offense count: 138
136
100
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
137
101
  Metrics/CyclomaticComplexity:
138
102
  Enabled: false
139
103
 
140
- # Offense count: 256
104
+ # Offense count: 276
141
105
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
142
106
  Metrics/MethodLength:
143
107
  Max: 154
144
108
 
145
- # Offense count: 6
109
+ # Offense count: 8
146
110
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
147
111
  Metrics/ParameterLists:
148
112
  Max: 9
149
113
 
150
- # Offense count: 99
114
+ # Offense count: 112
151
115
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
152
116
  Metrics/PerceivedComplexity:
153
117
  Enabled: false
@@ -171,12 +135,6 @@ Performance/CollectionLiteralInLoop:
171
135
  - 'lib/svg_conform/batch_report.rb'
172
136
  - 'lib/svg_conform/compatibility/pattern_discovery.rb'
173
137
 
174
- # Offense count: 2
175
- # This cop supports unsafe autocorrection (--autocorrect-all).
176
- Performance/MapCompact:
177
- Exclude:
178
- - 'lib/svg_conform/remediations/viewbox_remediation.rb'
179
-
180
138
  # Offense count: 9
181
139
  # Configuration parameters: IgnoredMetadata.
182
140
  RSpec/DescribeClass:
@@ -199,7 +157,7 @@ RSpec/DescribeClass:
199
157
  # Offense count: 157
200
158
  # Configuration parameters: CountAsOne.
201
159
  RSpec/ExampleLength:
202
- Max: 53
160
+ Max: 43
203
161
 
204
162
  # Offense count: 3
205
163
  RSpec/LeakyConstantDeclaration:
@@ -226,10 +184,19 @@ RSpec/MultipleDescribes:
226
184
  RSpec/MultipleExpectations:
227
185
  Max: 8
228
186
 
229
- # Offense count: 44
187
+ # Offense count: 11
230
188
  # This cop supports unsafe autocorrection (--autocorrect-all).
231
189
  RSpec/Output:
232
- Enabled: false
190
+ Exclude:
191
+ - 'spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb'
192
+ - 'spec/svg_conform/remediations/color_remediation_spec.rb'
193
+ - 'spec/svg_conform/remediations/font_remediation_spec.rb'
194
+ - 'spec/svg_conform/remediations/invalid_id_references_remediation_spec.rb'
195
+ - 'spec/svg_conform/remediations/namespace_attribute_remediation_spec.rb'
196
+ - 'spec/svg_conform/remediations/namespace_remediation_spec.rb'
197
+ - 'spec/svg_conform/remediations/no_external_css_remediation_spec.rb'
198
+ - 'spec/svg_conform/remediations/style_promotion_remediation_spec.rb'
199
+ - 'spec/svg_conform/remediations/viewbox_remediation_spec.rb'
233
200
 
234
201
  # Offense count: 1
235
202
  Rake/MethodDefinitionInTask:
@@ -251,20 +218,13 @@ Style/EmptyElse:
251
218
  - 'lib/svg_conform/remediations/namespace_remediation.rb'
252
219
  - 'lib/svg_conform/requirements/style_requirement.rb'
253
220
 
254
- # Offense count: 23
221
+ # Offense count: 13
255
222
  # This cop supports safe autocorrection (--autocorrect).
256
223
  # Configuration parameters: MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns.
257
224
  # SupportedStyles: annotated, template, unannotated
258
225
  Style/FormatStringToken:
259
226
  EnforcedStyle: unannotated
260
227
 
261
- # Offense count: 1
262
- # This cop supports unsafe autocorrection (--autocorrect-all).
263
- # Configuration parameters: AllowSplatArgument.
264
- Style/HashConversion:
265
- Exclude:
266
- - 'lib/svg_conform/sax_validation_handler.rb'
267
-
268
228
  # Offense count: 2
269
229
  # This cop supports unsafe autocorrection (--autocorrect-all).
270
230
  Style/HashSlice:
@@ -289,18 +249,6 @@ Style/OptionalBooleanParameter:
289
249
  Exclude:
290
250
  - 'lib/svg_conform/semantic_comparator.rb'
291
251
 
292
- # Offense count: 1
293
- # This cop supports unsafe autocorrection (--autocorrect-all).
294
- Style/PartitionInsteadOfDoubleSelect:
295
- Exclude:
296
- - 'lib/svg_conform/requirements/style_requirement.rb'
297
-
298
- # Offense count: 1
299
- # This cop supports unsafe autocorrection (--autocorrect-all).
300
- Style/ReduceToHash:
301
- Exclude:
302
- - 'lib/svg_conform/requirements/base_requirement.rb'
303
-
304
252
  # Offense count: 1
305
253
  # This cop supports safe autocorrection (--autocorrect).
306
254
  # Configuration parameters: AllowedMethods.
@@ -308,27 +256,3 @@ Style/ReduceToHash:
308
256
  Style/RedundantCondition:
309
257
  Exclude:
310
258
  - 'lib/svg_conform/external_checkers/svgcheck/parser.rb'
311
-
312
- # Offense count: 2
313
- # This cop supports unsafe autocorrection (--autocorrect-all).
314
- Style/RedundantFormat:
315
- Exclude:
316
- - 'lib/svg_conform/compatibility/report_formatter.rb'
317
-
318
- # Offense count: 2
319
- # This cop supports unsafe autocorrection (--autocorrect-all).
320
- Style/SelectByKind:
321
- Exclude:
322
- - 'lib/svg_conform/sax_validation_handler.rb'
323
-
324
- # Offense count: 1
325
- # This cop supports unsafe autocorrection (--autocorrect-all).
326
- Style/SelectByRegexp:
327
- Exclude:
328
- - 'lib/svg_conform/remediations/color_remediation.rb'
329
-
330
- # Offense count: 1
331
- # This cop supports safe autocorrection (--autocorrect).
332
- Style/SuperArguments:
333
- Exclude:
334
- - 'lib/svg_conform/requirements/invalid_id_references_requirement.rb'
data/Gemfile CHANGED
@@ -6,6 +6,8 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "canon", "~>0.1.10"
9
+ gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
10
+ gem "moxml", github: "lutaml/moxml", branch: "main"
9
11
  gem "openssl", "~> 3.0"
10
12
  gem "rake"
11
13
  gem "rspec"
data/README.adoc CHANGED
@@ -33,6 +33,7 @@ SvgConform is distributed as a Ruby gem.
33
33
  * **100% SVGCheck compatibility**: Perfect error report matching with Python svgcheck tool
34
34
  * **Configurable RDF metadata support**: Choose strict or permissive RDF/Dublin Core handling
35
35
  * **Generic namespace handling**: Configuration-driven foreign namespace validation
36
+ * **Text-as-path detection**: Warns when text is rendered as outlined path glyphs (quality check)
36
37
 
37
38
 
38
39
  == Concepts
@@ -360,6 +361,168 @@ SvgConform provides compatibility with existing SVG validation tools:
360
361
  See link:docs/compatibility.adoc[Compatibility Documentation] for details.
361
362
 
362
363
 
364
+ [[image_quality]]
365
+ == Image quality metric
366
+
367
+ SvgConform provides a quality metric system for analyzing and scoring SVG files
368
+ across multiple dimensions:
369
+
370
+ * **Quality Score (0-100)**: Composite score based on error density, severity, and remediability
371
+ * **Quality Level**: EXCELLENT, GOOD, FAIR, POOR, or CRITICAL
372
+ * **Error Breakdown**: Counts by severity (critical, high, medium, low) and remediability
373
+ * **Complexity Index**: 1-10 scale based on element count and advanced features
374
+ * **Feature Detection**: Base64 images, foreign namespaces, masks, clip paths, external refs
375
+
376
+ Reports are fully object-oriented: the same report object supports `to_yaml`, `to_json`,
377
+ and colorful terminal rendering.
378
+
379
+ [IMPORTANT]
380
+ .Quality depends on validation profile
381
+ ====
382
+ The quality score is calculated based on validation errors, which depend on the
383
+ chosen profile. Different profiles have different rules, so the same SVG file
384
+ may receive different quality scores under different profiles.
385
+
386
+ Example: `colors.svg` scores 60 (FAIR) under `svg_1_2_rfc` but 100 (EXCELLENT)
387
+ under `metanorma` because the profiles have different color restrictions.
388
+ ====
389
+
390
+ [source,bash]
391
+ ----
392
+ # Integrated into main CLI (svg_conform quality analyze)
393
+ svg_conform quality analyze image.svg
394
+
395
+ # Output as YAML
396
+ svg_conform quality analyze image.svg -- --format yaml
397
+
398
+ # Batch analyze a directory
399
+ svg_conform quality batch ./svgs -- --output quality_report.csv
400
+
401
+ # With different profile (affects quality score!)
402
+ svg_conform quality analyze image.svg -- --profile metanorma
403
+ ----
404
+
405
+ [source,ruby]
406
+ ----
407
+ # Single file analysis - returns SvgQualityReport object
408
+ analyzer = SvgConform::ImageQualityAnalyzer.new
409
+ report = analyzer.analyze('image.svg')
410
+
411
+ # Three output formats from the same object
412
+ puts report.render # Colorful terminal output
413
+ puts report.to_yaml # YAML serialization
414
+ puts report.to_json # JSON serialization
415
+
416
+ # Access attributes directly
417
+ puts "Quality: #{report.quality_score} (#{report.quality_level})"
418
+ puts "Errors: #{report.error_count} (#{report.remediable_errors} remediable)"
419
+ puts "Complexity: #{report.complexity_index}/10"
420
+
421
+ # Batch analysis - returns SvgQualityBatchReport object
422
+ batch = analyzer.analyze_batch('./svgs')
423
+ puts batch.render # Summary with distribution chart
424
+ puts batch.to_yaml # Full YAML with all reports
425
+
426
+ # Filter and process reports
427
+ poor_files = batch.reports.select { |r| r.quality_level == :poor }
428
+ ----
429
+
430
+ For detailed documentation including custom configuration and CLI options, see
431
+ link:docs/image_quality.adoc[Image Quality Metric Documentation].
432
+
433
+ === CLI output examples
434
+
435
+ .Single file analysis with default profile (svg_1_2_rfc)
436
+ [source,text]
437
+ ----
438
+ $ svg_conform quality analyze spec/fixtures/svgcheck/inputs/colors.svg
439
+
440
+ 📊 SVG Quality Report: colors.svg
441
+
442
+ ⚠️ Quality: 60 (FAIR)
443
+ 🔶 Content Health: moderate_issues
444
+
445
+ 📁 File: 1.41 KB (small)
446
+ 📐 Elements: 17
447
+ 🔢 Complexity: 1.0/10
448
+
449
+ Errors: 12
450
+ 🔧 Remediable: 12
451
+ ⚠️ Non-remediable: 0
452
+
453
+ Breakdown:
454
+ ● Medium: 12
455
+ ----
456
+
457
+ .Same file with metanorma profile (no color restrictions)
458
+ [source,text]
459
+ ----
460
+ $ svg_conform quality analyze spec/fixtures/svgcheck/inputs/colors.svg -- --profile metanorma
461
+
462
+ 📊 SVG Quality Report: colors.svg
463
+
464
+ ✨ Quality: 100 (EXCELLENT)
465
+ ✅ Content Health: good
466
+
467
+ 📁 File: 1.41 KB (small)
468
+ 📐 Elements: 17
469
+ 🔢 Complexity: 1.0/10
470
+
471
+ Errors: 0
472
+ 🔧 Remediable: 0
473
+ ⚠️ Non-remediable: 0
474
+ ----
475
+
476
+ .Single file analysis (YAML output)
477
+ [source,yaml]
478
+ ----
479
+ $ svg_conform quality analyze spec/fixtures/svgcheck/inputs/colors.svg -- --format yaml
480
+
481
+ ---
482
+ file_path: spec/fixtures/svgcheck/inputs/colors.svg
483
+ quality_score: 60
484
+ quality_level: :fair
485
+ error_count: 12
486
+ remediable_errors: 12
487
+ ...
488
+ ----
489
+
490
+ .Single file analysis (JSON output)
491
+ [source,json]
492
+ ----
493
+ $ svg_conform quality analyze spec/fixtures/svgcheck/inputs/colors.svg -- --format json
494
+
495
+ {"file_path":"spec/fixtures/svgcheck/inputs/colors.svg","quality_score":60,...}
496
+ ----
497
+
498
+ .Batch summary (terminal output with distribution chart)
499
+ [source,text]
500
+ ----
501
+ $ svg_conform quality batch spec/fixtures/svgcheck/inputs -- --summary-only
502
+
503
+ 📊 SVG Quality Batch Report
504
+
505
+ 📁 Total Files: 19
506
+ ✅ Successful: 19
507
+
508
+ Quality Distribution
509
+
510
+ ✨ EXCELLENT ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2 (10.5%)
511
+ 👍 GOOD ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2 (10.5%)
512
+ ⚠️ FAIR ████████████████████████████████████████ 13 (68.4%)
513
+ 😟 POOR ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2 (10.5%)
514
+ 💥 CRITICAL ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0 (0.0%)
515
+
516
+ Summary
517
+
518
+ 📈 Average Quality Score: 61.6/100
519
+ 📊 Average Errors/File: 204.3
520
+
521
+ 🔧 Total Remediable: 3872
522
+ ⚠️ Total Non-remediable: 10
523
+ ----
524
+
525
+
363
526
  == Documentation
364
527
 
365
528
  === User documentation
@@ -371,6 +534,7 @@ See link:docs/compatibility.adoc[Compatibility Documentation] for details.
371
534
  * link:docs/remediation.adoc[Remediation Reference] - All automatic remediations
372
535
  * link:docs/reference_manifest.adoc[Reference Manifest] - ID and reference tracking
373
536
  * link:docs/rdf_metadata_support.adoc[RDF Metadata Support] - RDF/Dublin Core configuration
537
+ * link:docs/image_quality.adoc[Image Quality Metric] - Quality scoring and analysis
374
538
 
375
539
  === Developer documentation
376
540
 
@@ -21,7 +21,7 @@ requirements:
21
21
  element_configs:
22
22
  # Direct encoding from svgcheck word_properties.py elements dictionary
23
23
  - tag: "svg"
24
- attributes: ["version", "baseProfile", "width", "viewBox", "preserveAspectRatio", "snapshotTime", "height", "id", "role", "break", "color-rendering", "fill-rule", "overflow", "data-name"]
24
+ attributes: ["version", "baseProfile", "width", "viewBox", "preserveAspectRatio", "snapshotTime", "height", "id", "role", "break", "color-rendering", "fill-rule", "overflow", "data-name", "clip-path", "mask"]
25
25
  required_attributes: ["version", "baseProfile"]
26
26
  attribute_values:
27
27
  version: ["1.2"]
@@ -34,37 +34,37 @@ requirements:
34
34
  attributes: ["id", "role", "shape-rendering", "text-rendering", "buffered-rendering", "visibility", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "fill-rule"]
35
35
  allowed_children: ["text"]
36
36
  - tag: "path"
37
- attributes: ["d", "pathLength", "stroke-miterlimit", "id", "role", "fill", "style", "transform", "font-size", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
37
+ attributes: ["d", "pathLength", "stroke-miterlimit", "id", "role", "fill", "style", "transform", "font-size", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
38
38
  allowed_children: ["title", "desc"]
39
39
  - tag: "rect"
40
- attributes: ["x", "y", "width", "height", "rx", "ry", "stroke-miterlimit", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
40
+ attributes: ["x", "y", "width", "height", "rx", "ry", "stroke-miterlimit", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
41
41
  allowed_children: ["title", "desc"]
42
42
  - tag: "circle"
43
- attributes: ["cx", "cy", "r", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
43
+ attributes: ["cx", "cy", "r", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
44
44
  allowed_children: ["title", "desc"]
45
45
  - tag: "line"
46
- attributes: ["x1", "y1", "x2", "y2", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
46
+ attributes: ["x1", "y1", "x2", "y2", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
47
47
  allowed_children: ["title", "desc"]
48
48
  - tag: "ellipse"
49
- attributes: ["cx", "cy", "rx", "ry", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
49
+ attributes: ["cx", "cy", "rx", "ry", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
50
50
  allowed_children: ["title", "desc"]
51
51
  - tag: "polyline"
52
- attributes: ["points", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
52
+ attributes: ["points", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
53
53
  allowed_children: ["title", "desc"]
54
54
  - tag: "polygon"
55
- attributes: ["points", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
55
+ attributes: ["points", "id", "role", "fill", "style", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
56
56
  allowed_children: ["title", "desc"]
57
57
  - tag: "solidColor"
58
- attributes: ["id", "role", "fill", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
58
+ attributes: ["id", "role", "fill", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
59
59
  allowed_children: ["title", "desc"]
60
60
  - tag: "textArea"
61
- attributes: ["x", "y", "width", "height", "auto", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
61
+ attributes: ["x", "y", "width", "height", "auto", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
62
62
  allowed_children: ["desc", "title", "tspan", "text", "a"]
63
63
  - tag: "text"
64
- attributes: ["x", "y", "rotate", "id", "role", "fill", "style", "transform", "font-size", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "font-stretch", "writing-mode", "text-decoration"]
64
+ attributes: ["x", "y", "rotate", "id", "role", "fill", "style", "transform", "font-size", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "font-stretch", "writing-mode", "text-decoration", "clip-path", "mask"]
65
65
  allowed_children: ["desc", "title", "tspan", "text", "a"]
66
66
  - tag: "g"
67
- attributes: ["label", "class", "id", "role", "fill", "style", "transform", "fill-rule", "visibility", "base", "lang", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "data-name"]
67
+ attributes: ["label", "class", "id", "role", "fill", "style", "transform", "fill-rule", "visibility", "base", "lang", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "data-name", "clip-path", "mask"]
68
68
  allowed_children: ["title", "path", "rect", "circle", "line", "ellipse", "polyline", "polygon", "solidColor", "textArea", "text", "g", "defs", "use", "a", "tspan", "desc", "image"]
69
69
  - tag: "defs"
70
70
  attributes: ["id", "role", "fill", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
@@ -97,7 +97,7 @@ requirements:
97
97
  attributes: ["type", "media", "title", "id", "class"]
98
98
  allowed_children: []
99
99
  - tag: "use"
100
- attributes: ["x", "y", "href", "xlink:href", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
100
+ attributes: ["x", "y", "href", "xlink:href", "id", "role", "fill", "transform", "fill-rule", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space", "clip-path", "mask"]
101
101
  allowed_children: ["title", "desc"]
102
102
  - tag: "a"
103
103
  attributes: ["id", "role", "fill", "transform", "fill-rule", "target", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
@@ -108,7 +108,7 @@ requirements:
108
108
  - tag: "tbreak"
109
109
  attributes: ["id", "role", "base", "lang", "class", "rel", "rev", "typeof", "content", "datatype", "resource", "about", "property", "space"]
110
110
  - tag: "image"
111
- attributes: ["x", "y", "width", "height", "href", "xlink:href", "preserveAspectRatio", "id", "class", "style", "transform"]
111
+ attributes: ["x", "y", "width", "height", "href", "xlink:href", "preserveAspectRatio", "id", "class", "style", "transform", "clip-path", "mask"]
112
112
  allowed_children: ["title", "desc"]
113
113
  check_attributes: true
114
114
  check_invalid_attributes: true
@@ -187,6 +187,13 @@ requirements:
187
187
  check_image_elements: true
188
188
  check_style_images: true
189
189
 
190
+ # Text-as-path detection - warns when text is rendered as outlined paths
191
+ - id: "text_as_path"
192
+ type: "TextAsPathRequirement"
193
+ description: "Detects text rendered as outlined paths instead of native <text> elements"
194
+ min_d_length: 500
195
+ min_bezier: 5
196
+
190
197
  remediations:
191
198
  # ViewBox auto-generation
192
199
  - id: "viewbox_generation"