svg_conform 0.1.2 → 0.1.4

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +17 -239
  3. data/README.adoc +55 -0
  4. data/lib/svg_conform/element_proxy.rb +10 -10
  5. data/lib/svg_conform/fast_document_analyzer.rb +22 -22
  6. data/lib/svg_conform/node_index_builder.rb +1 -0
  7. data/lib/svg_conform/profile.rb +23 -8
  8. data/lib/svg_conform/requirements/allowed_elements_requirement.rb +29 -9
  9. data/lib/svg_conform/requirements/color_restrictions_requirement.rb +8 -6
  10. data/lib/svg_conform/requirements/font_family_requirement.rb +6 -2
  11. data/lib/svg_conform/requirements/forbidden_content_requirement.rb +2 -2
  12. data/lib/svg_conform/requirements/id_reference_requirement.rb +10 -7
  13. data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +7 -6
  14. data/lib/svg_conform/requirements/link_validation_requirement.rb +2 -2
  15. data/lib/svg_conform/requirements/namespace_attributes_requirement.rb +16 -10
  16. data/lib/svg_conform/requirements/namespace_requirement.rb +11 -10
  17. data/lib/svg_conform/requirements/no_external_css_requirement.rb +32 -5
  18. data/lib/svg_conform/requirements/no_external_fonts_requirement.rb +2 -2
  19. data/lib/svg_conform/requirements/no_external_images_requirement.rb +2 -2
  20. data/lib/svg_conform/requirements/style_promotion_requirement.rb +7 -0
  21. data/lib/svg_conform/requirements/viewbox_required_requirement.rb +7 -7
  22. data/lib/svg_conform/sax_document.rb +2 -2
  23. data/lib/svg_conform/sax_validation_handler.rb +14 -10
  24. data/lib/svg_conform/validation_context.rb +9 -7
  25. data/lib/svg_conform/validator.rb +24 -5
  26. data/lib/svg_conform/version.rb +1 -1
  27. data/spec/spec_helper.rb +3 -0
  28. data/spec/support/shared_examples_for_validation_modes.rb +71 -0
  29. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43ff84b9d1f47a6a2bd92624483c0445bd3f77d29f0fc36f2c02116a40111bc2
4
- data.tar.gz: be072c8772885cd746ee623e028e00517dfcac93250a00f4015cb7122d94739a
3
+ metadata.gz: 24fb1d016b5e3ed96b2da518c95884fac16a47b18cd8566e129d81b49791fb32
4
+ data.tar.gz: ad67c207d5f1489855772d6c8931ea4dd8277b041a2a791ceebb6fe2af63ff36
5
5
  SHA512:
6
- metadata.gz: 0ee8ad3bfab5a2aec10b32caa23141967aa25112e0393ed39242ab83a5f123bb317190392df347f7f4a8665af61bf7de4b09eb3283c4c140e372af1177ba924b
7
- data.tar.gz: 20d1871024c131298aa2462bd849e07dfb16b73f2f61913da8f9c2d21b43e87fcfe5ae6ed298971091c0f85734efe8a36790432ccdb18c8878dafc25d238a348
6
+ metadata.gz: fa0cdaf7dcd0ed01c23d334ed2d92e1674954a9fce8aed9aad85fd884a2949a6da81cec006607005971d61093384076d2797f72204a7362bad94a8fb882712e2
7
+ data.tar.gz: c6e9af1cba1fb25edbf89ed1ddc77261b4c8a3d068f79d1b56553d5636114f8a493b48dcbaa8a125c99319a043cbfbc5737be77dbe6054f9145a02ddeb94e37a
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-11-18 13:36:55 UTC using RuboCop version 1.81.1.
3
+ # on 2025-11-19 10:52:11 UTC using RuboCop version 1.81.1.
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
@@ -16,61 +16,10 @@ Layout/BlockAlignment:
16
16
 
17
17
  # Offense count: 1
18
18
  # This cop supports safe autocorrection (--autocorrect).
19
- # Configuration parameters: AllowForAlignment.
20
- Layout/CommentIndentation:
21
- Exclude:
22
- - 'lib/svg_conform/validation_context.rb'
23
-
24
- # Offense count: 1
25
- # This cop supports safe autocorrection (--autocorrect).
26
- Layout/ElseAlignment:
27
- Exclude:
28
- - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
29
-
30
- # Offense count: 17
31
- # This cop supports safe autocorrection (--autocorrect).
32
- Layout/EmptyLineAfterGuardClause:
33
- Exclude:
34
- - 'analyze_error_nodes.rb'
35
- - 'check_duplicates.rb'
36
- - 'check_position_collisions.rb'
37
- - 'check_unique_positions.rb'
38
- - 'compare_errors.rb'
39
- - 'debug_object_ids.rb'
40
- - 'debug_utf8.rb'
41
- - 'find_all_elements.rb'
42
- - 'find_missing_errors.rb'
43
- - 'lib/svg_conform/fast_document_analyzer.rb'
44
- - 'lib/svg_conform/node_index_builder.rb'
45
- - 'lib/svg_conform/requirements/id_reference_requirement.rb'
46
- - 'lib/svg_conform/sax_validation_handler.rb'
47
-
48
- # Offense count: 1
49
- # This cop supports safe autocorrection (--autocorrect).
50
- # Configuration parameters: EnforcedStyleAlignWith, Severity.
51
- # SupportedStylesAlignWith: keyword, variable, start_of_line
52
- Layout/EndAlignment:
53
- Exclude:
54
- - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
55
-
56
- # Offense count: 21
57
- # This cop supports safe autocorrection (--autocorrect).
58
19
  # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
59
20
  Layout/ExtraSpacing:
60
21
  Exclude:
61
- - 'find_missing_errors.rb'
62
- - 'lib/svg_conform/element_proxy.rb'
63
- - 'lib/svg_conform/requirements/id_reference_requirement.rb'
64
- - 'lib/svg_conform/requirements/invalid_id_references_requirement.rb'
65
- - 'lib/svg_conform/sax_validation_handler.rb'
66
- - 'lib/svg_conform/validation_context.rb'
67
- - 'lib/svg_conform/validator.rb'
68
-
69
- # Offense count: 1
70
- # This cop supports safe autocorrection (--autocorrect).
71
- Layout/HeredocIndentation:
72
- Exclude:
73
- - 'test_sax_basic.rb'
22
+ - 'lib/svg_conform/requirements/no_external_css_requirement.rb'
74
23
 
75
24
  # Offense count: 1
76
25
  # This cop supports safe autocorrection (--autocorrect).
@@ -78,52 +27,15 @@ Layout/HeredocIndentation:
78
27
  # SupportedStyles: normal, indented_internal_methods
79
28
  Layout/IndentationConsistency:
80
29
  Exclude:
81
- - 'lib/svg_conform/validation_context.rb'
82
-
83
- # Offense count: 1
84
- # This cop supports safe autocorrection (--autocorrect).
85
- # Configuration parameters: Width, AllowedPatterns.
86
- Layout/IndentationWidth:
87
- Exclude:
88
- - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
30
+ - 'lib/svg_conform/profile.rb'
89
31
 
90
- # Offense count: 604
32
+ # Offense count: 616
91
33
  # This cop supports safe autocorrection (--autocorrect).
92
34
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
93
35
  # URISchemes: http, https
94
36
  Layout/LineLength:
95
37
  Enabled: false
96
38
 
97
- # Offense count: 28
98
- # This cop supports safe autocorrection (--autocorrect).
99
- # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
100
- # SupportedStylesForExponentOperator: space, no_space
101
- # SupportedStylesForRationalLiterals: space, no_space
102
- Layout/SpaceAroundOperators:
103
- Exclude:
104
- - 'benchmark_sax.rb'
105
- - 'benchmark_validation.rb'
106
- - 'check_clippath_font.rb'
107
- - 'check_unique_positions.rb'
108
- - 'count_elements.rb'
109
- - 'final_demo.rb'
110
- - 'test_remediation.rb'
111
-
112
- # Offense count: 32
113
- # This cop supports safe autocorrection (--autocorrect).
114
- # Configuration parameters: AllowInHeredoc.
115
- Layout/TrailingWhitespace:
116
- Exclude:
117
- - 'analyze_error_nodes.rb'
118
- - 'check_clippath_font.rb'
119
- - 'debug_positions.rb'
120
- - 'debug_utf8.rb'
121
- - 'final_demo.rb'
122
- - 'find_missing_errors.rb'
123
- - 'lib/svg_conform/fast_document_analyzer.rb'
124
- - 'lib/svg_conform/sax_document.rb'
125
- - 'test_sax_basic.rb'
126
-
127
39
  # Offense count: 2
128
40
  # Configuration parameters: AllowedMethods.
129
41
  # AllowedMethods: enums
@@ -131,10 +43,11 @@ Lint/ConstantDefinitionInBlock:
131
43
  Exclude:
132
44
  - 'spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb'
133
45
 
134
- # Offense count: 19
46
+ # Offense count: 22
135
47
  # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
136
48
  Lint/DuplicateBranch:
137
49
  Exclude:
50
+ - 'lib/svg_conform/profile.rb'
138
51
  - 'lib/svg_conform/remediations/font_embedding_remediation.rb'
139
52
  - 'lib/svg_conform/remediations/image_embedding_remediation.rb'
140
53
  - 'lib/svg_conform/report_comparator.rb'
@@ -154,12 +67,6 @@ Lint/DuplicateMethods:
154
67
  - 'lib/svg_conform/sax_validation_handler.rb'
155
68
  - 'lib/svg_conform/validation_context.rb'
156
69
 
157
- # Offense count: 1
158
- # This cop supports safe autocorrection (--autocorrect).
159
- Lint/IdentityComparison:
160
- Exclude:
161
- - 'lib/svg_conform/fast_document_analyzer.rb'
162
-
163
70
  # Offense count: 4
164
71
  # Configuration parameters: AllowedParentClasses.
165
72
  Lint/MissingSuper:
@@ -168,43 +75,25 @@ Lint/MissingSuper:
168
75
  - 'lib/svg_conform/compatibility/validity_analysis.rb'
169
76
  - 'lib/svg_conform/sax_validation_handler.rb'
170
77
 
171
- # Offense count: 3
172
- # This cop supports safe autocorrection (--autocorrect).
173
- Lint/ScriptPermission:
174
- Exclude:
175
- - 'benchmark_validation.rb'
176
- - 'final_demo.rb'
177
- - 'test_remediation.rb'
178
-
179
78
  # Offense count: 1
180
79
  Lint/UnreachableCode:
181
80
  Exclude:
182
81
  - 'lib/svg_conform/commands/check.rb'
183
82
 
184
- # Offense count: 4
83
+ # Offense count: 1
185
84
  # This cop supports safe autocorrection (--autocorrect).
186
85
  # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
187
86
  # NotImplementedExceptions: NotImplementedError
188
87
  Lint/UnusedMethodArgument:
189
88
  Exclude:
190
- - 'lib/svg_conform/element_proxy.rb'
191
- - 'lib/svg_conform/requirements/id_reference_requirement.rb'
192
- - 'lib/svg_conform/requirements/invalid_id_references_requirement.rb'
193
- - 'lib/svg_conform/sax_validation_handler.rb'
194
-
195
- # Offense count: 1
196
- # This cop supports safe autocorrection (--autocorrect).
197
- # Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
198
- Lint/UselessAccessModifier:
199
- Exclude:
200
- - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
89
+ - 'lib/svg_conform/requirements/no_external_css_requirement.rb'
201
90
 
202
- # Offense count: 147
91
+ # Offense count: 148
203
92
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
204
93
  Metrics/AbcSize:
205
94
  Enabled: false
206
95
 
207
- # Offense count: 21
96
+ # Offense count: 22
208
97
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
209
98
  # AllowedMethods: refine
210
99
  Metrics/BlockLength:
@@ -215,12 +104,12 @@ Metrics/BlockLength:
215
104
  Metrics/BlockNesting:
216
105
  Max: 4
217
106
 
218
- # Offense count: 125
107
+ # Offense count: 126
219
108
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
220
109
  Metrics/CyclomaticComplexity:
221
110
  Enabled: false
222
111
 
223
- # Offense count: 257
112
+ # Offense count: 261
224
113
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
225
114
  Metrics/MethodLength:
226
115
  Max: 154
@@ -249,12 +138,6 @@ Performance/CollectionLiteralInLoop:
249
138
  - 'lib/svg_conform/batch_report.rb'
250
139
  - 'lib/svg_conform/compatibility/pattern_discovery.rb'
251
140
 
252
- # Offense count: 1
253
- # This cop supports unsafe autocorrection (--autocorrect-all).
254
- Performance/UnfreezeString:
255
- Exclude:
256
- - 'lib/svg_conform/element_proxy.rb'
257
-
258
141
  # Offense count: 8
259
142
  # Configuration parameters: IgnoredMetadata.
260
143
  RSpec/DescribeClass:
@@ -273,7 +156,7 @@ RSpec/DescribeClass:
273
156
  - 'spec/svg_conform/profiles/svg_1_2_rfc_with_rdf_profile_spec.rb'
274
157
  - 'spec/svgcheck_compatibility_spec.rb'
275
158
 
276
- # Offense count: 106
159
+ # Offense count: 109
277
160
  # Configuration parameters: CountAsOne.
278
161
  RSpec/ExampleLength:
279
162
  Max: 53
@@ -288,7 +171,7 @@ RSpec/MultipleDescribes:
288
171
  Exclude:
289
172
  - 'spec/svg_conform/batch_report_spec.rb'
290
173
 
291
- # Offense count: 71
174
+ # Offense count: 73
292
175
  RSpec/MultipleExpectations:
293
176
  Max: 6
294
177
 
@@ -297,14 +180,6 @@ Rake/MethodDefinitionInTask:
297
180
  Exclude:
298
181
  - 'lib/tasks/svgcheck.rake'
299
182
 
300
- # Offense count: 3
301
- # This cop supports unsafe autocorrection (--autocorrect-all).
302
- Style/CombinableLoops:
303
- Exclude:
304
- - 'check_duplicates.rb'
305
- - 'compare_errors.rb'
306
- - 'find_missing_errors.rb'
307
-
308
183
  # Offense count: 2
309
184
  # This cop supports safe autocorrection (--autocorrect).
310
185
  # Configuration parameters: EnforcedStyle, AllowComments.
@@ -326,19 +201,6 @@ Style/MissingRespondToMissing:
326
201
  Exclude:
327
202
  - 'lib/svg_conform/cli.rb'
328
203
 
329
- # Offense count: 6
330
- # This cop supports unsafe autocorrection (--autocorrect-all).
331
- # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
332
- # SupportedStyles: predicate, comparison
333
- Style/NumericPredicate:
334
- Exclude:
335
- - 'spec/**/*'
336
- - 'benchmark_validation.rb'
337
- - 'check_unique_positions.rb'
338
- - 'lib/svg_conform/validation_context.rb'
339
- - 'test_remediation.rb'
340
- - 'test_sax_basic.rb'
341
-
342
204
  # Offense count: 4
343
205
  # Configuration parameters: AllowedMethods.
344
206
  # AllowedMethods: respond_to_missing?
@@ -346,12 +208,6 @@ Style/OptionalBooleanParameter:
346
208
  Exclude:
347
209
  - 'lib/svg_conform/semantic_comparator.rb'
348
210
 
349
- # Offense count: 1
350
- # This cop supports safe autocorrection (--autocorrect).
351
- Style/RedundantAssignment:
352
- Exclude:
353
- - 'lib/svg_conform/fast_document_analyzer.rb'
354
-
355
211
  # Offense count: 1
356
212
  # This cop supports safe autocorrection (--autocorrect).
357
213
  # Configuration parameters: AllowedMethods.
@@ -360,87 +216,9 @@ Style/RedundantCondition:
360
216
  Exclude:
361
217
  - 'lib/svg_conform/external_checkers/svgcheck/parser.rb'
362
218
 
363
- # Offense count: 1
364
- # This cop supports unsafe autocorrection (--autocorrect-all).
365
- Style/RedundantInterpolation:
366
- Exclude:
367
- - 'analyze_error_nodes.rb'
368
-
369
- # Offense count: 2
370
- # This cop supports safe autocorrection (--autocorrect).
371
- # Configuration parameters: EnforcedStyle.
372
- # SupportedStyles: implicit, explicit
373
- Style/RescueStandardError:
374
- Exclude:
375
- - 'debug_node_coverage.rb'
376
- - 'test_sax_basic.rb'
377
-
378
219
  # Offense count: 3
379
- # This cop supports unsafe autocorrection (--autocorrect-all).
380
- # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
381
- # AllowedMethods: present?, blank?, presence, try, try!
382
- Style/SafeNavigation:
383
- Exclude:
384
- - 'count_elements.rb'
385
- - 'lib/svg_conform/fast_document_analyzer.rb'
386
-
387
- # Offense count: 4
388
- # This cop supports unsafe autocorrection (--autocorrect-all).
389
- # Configuration parameters: Mode.
390
- Style/StringConcatenation:
391
- Exclude:
392
- - 'benchmark_sax.rb'
393
- - 'benchmark_validation.rb'
394
- - 'final_demo.rb'
395
- - 'test_remediation.rb'
396
-
397
- # Offense count: 78
398
- # This cop supports safe autocorrection (--autocorrect).
399
- # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
400
- # SupportedStyles: single_quotes, double_quotes
401
- Style/StringLiterals:
402
- Enabled: false
403
-
404
- # Offense count: 34
405
220
  # This cop supports safe autocorrection (--autocorrect).
406
- # Configuration parameters: EnforcedStyleForMultiline.
407
- # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
408
- Style/TrailingCommaInArguments:
221
+ # Configuration parameters: AllowMultipleReturnValues.
222
+ Style/RedundantReturn:
409
223
  Exclude:
410
- - 'lib/svg_conform/requirements/allowed_elements_requirement.rb'
411
- - 'lib/svg_conform/requirements/color_restrictions_requirement.rb'
412
- - 'lib/svg_conform/requirements/font_family_requirement.rb'
413
- - 'lib/svg_conform/requirements/forbidden_content_requirement.rb'
414
- - 'lib/svg_conform/requirements/id_reference_requirement.rb'
415
- - 'lib/svg_conform/requirements/invalid_id_references_requirement.rb'
416
- - 'lib/svg_conform/requirements/link_validation_requirement.rb'
417
- - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
418
- - 'lib/svg_conform/requirements/namespace_requirement.rb'
419
- - 'lib/svg_conform/requirements/no_external_css_requirement.rb'
420
- - 'lib/svg_conform/requirements/no_external_fonts_requirement.rb'
421
- - 'lib/svg_conform/requirements/no_external_images_requirement.rb'
422
- - 'lib/svg_conform/requirements/viewbox_required_requirement.rb'
423
- - 'lib/svg_conform/sax_validation_handler.rb'
424
- - 'lib/svg_conform/validator.rb'
425
-
426
- # Offense count: 12
427
- # This cop supports safe autocorrection (--autocorrect).
428
- # Configuration parameters: EnforcedStyleForMultiline.
429
- # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
430
- Style/TrailingCommaInHashLiteral:
431
- Exclude:
432
- - 'debug_positions.rb'
433
- - 'lib/svg_conform/requirements/allowed_elements_requirement.rb'
434
- - 'lib/svg_conform/requirements/color_restrictions_requirement.rb'
435
- - 'lib/svg_conform/requirements/namespace_requirement.rb'
436
- - 'lib/svg_conform/requirements/viewbox_required_requirement.rb'
437
- - 'lib/svg_conform/validator.rb'
438
-
439
- # Offense count: 4
440
- # This cop supports unsafe autocorrection (--autocorrect-all).
441
- Style/ZeroLengthPredicate:
442
- Exclude:
443
- - 'benchmark_validation.rb'
444
- - 'lib/svg_conform/validation_context.rb'
445
- - 'test_remediation.rb'
446
- - 'test_sax_basic.rb'
224
+ - 'lib/svg_conform/profile.rb'
data/README.adoc CHANGED
@@ -1364,6 +1364,61 @@ $ svg_conform svgcheck compare diagram.svg --svgcheck-report diagram.svg.svgchec
1364
1364
  ====
1365
1365
 
1366
1366
 
1367
+
1368
+ == Performance
1369
+
1370
+ === SAX streaming validation
1371
+
1372
+ SvgConform implements SAX (Simple API for XML) streaming validation for
1373
+ high-performance processing of large SVG files.
1374
+
1375
+ Performance characteristics:
1376
+
1377
+ * Small files (<1 MB): ~0.5s validation time
1378
+ * Medium files (1-5 MB): ~1s validation time (vs 60-80s with DOM)
1379
+ * Large files (>5 MB): ~2s validation time (vs minutes-hours with DOM)
1380
+
1381
+ Real-world samples results:
1382
+
1383
+ * 5.93 MB file: >` 60s → 1.89s (30x faster)
1384
+ * 4.57 MB file: >60s → 1.37s (40x faster)
1385
+ * 3.44 MB file: >60s → 0.29s (200x faster)
1386
+
1387
+ How it works:
1388
+
1389
+ SAX streaming validation processes XML events without building a full DOM tree
1390
+ in memory. This provides:
1391
+
1392
+ * Forward-only processing: Single pass through XML stream
1393
+ * Constant memory: No full DOM tree in memory
1394
+ * Predictable performance: O(n) complexity guaranteed
1395
+ * No object identity issues: Path-based node identification
1396
+
1397
+ Mode selection:
1398
+
1399
+ By default, SAX mode is used for all validation operations. DOM mode is
1400
+ automatically used when remediation is requested (`fix: true`).
1401
+
1402
+ [source,ruby]
1403
+ ----
1404
+ # Default: SAX mode (best performance)
1405
+ validator = SvgConform::Validator.new
1406
+ result = validator.validate_file('large.svg', profile: :metanorma)
1407
+
1408
+ # Explicit mode selection
1409
+ validator_sax = SvgConform::Validator.new(mode: :sax) # Force SAX
1410
+ validator_dom = SvgConform::Validator.new(mode: :dom) # Force DOM
1411
+ validator_auto = SvgConform::Validator.new(mode: :auto) # File size based (>1MB uses SAX)
1412
+ ----
1413
+
1414
+ Validation parity:
1415
+
1416
+ SAX mode produces identical validation results to DOM mode on 18/19 test files
1417
+ (94.7% parity). The one exception is `full-tiny.svg`, a comprehensive test file
1418
+ with known architectural differences.
1419
+
1420
+
1421
+
1367
1422
  == Testing
1368
1423
 
1369
1424
  Run the test suite:
@@ -11,7 +11,7 @@ module SvgConform
11
11
  end
12
12
 
13
13
  def namespace
14
- nil # Simplified for now
14
+ nil # DOM handles namespaces via XPath; SAX checks attribute names directly
15
15
  end
16
16
  end
17
17
 
@@ -24,12 +24,12 @@ module SvgConform
24
24
 
25
25
  def initialize(name:, attributes:, position:, path:, parent:)
26
26
  @name = name
27
- @raw_attributes = attributes # Hash of attribute name => value
27
+ @raw_attributes = attributes # Hash of attribute name => value
28
28
  @position = position
29
- @path = path # Array of parent path parts
29
+ @path = path # Array of parent path parts
30
30
  @parent = parent
31
- @text_content = String.new # Mutable string
32
- @child_counters = {} # Track child element positions
31
+ @text_content = +"" # Mutable string
32
+ @child_counters = {} # Track child element positions
33
33
  end
34
34
 
35
35
  # Build full path ID for this element
@@ -61,7 +61,7 @@ module SvgConform
61
61
 
62
62
  # Get namespace from attributes or parent
63
63
  def namespace
64
- @raw_attributes['xmlns'] || @parent&.namespace
64
+ @raw_attributes["xmlns"] || @parent&.namespace
65
65
  end
66
66
 
67
67
  # Check if this is a text node (always false for ElementProxy)
@@ -70,10 +70,10 @@ module SvgConform
70
70
  end
71
71
 
72
72
  # Support dynamic attribute access
73
- def method_missing(method, *args)
74
- if method.to_s.end_with?('?')
73
+ def method_missing(method, *_args)
74
+ if method.to_s.end_with?("?")
75
75
  # Boolean check
76
- has_attribute?(method.to_s.chomp('?'))
76
+ has_attribute?(method.to_s.chomp("?"))
77
77
  else
78
78
  # Attribute access
79
79
  @attributes[method.to_s] || @attributes[method.to_sym]
@@ -86,7 +86,7 @@ module SvgConform
86
86
 
87
87
  # For compatibility with validation context
88
88
  def line
89
- nil # SAX doesn't provide line numbers easily
89
+ nil # SAX doesn't provide line numbers easily
90
90
  end
91
91
 
92
92
  def column
@@ -3,79 +3,79 @@
3
3
  module SvgConform
4
4
  # Fast document analyzer that pre-computes node relationships and IDs
5
5
  # in a single pass to avoid repeated expensive traversals
6
- #
6
+ #
7
7
  # IMPORTANT: Stores paths indexed by the path itself (not object_id)
8
8
  # since node object_ids change between traversals
9
9
  class FastDocumentAnalyzer
10
10
  attr_reader :path_cache
11
-
11
+
12
12
  def initialize(document)
13
13
  @document = document
14
14
  @path_cache = {}
15
-
15
+
16
16
  analyze_document
17
17
  end
18
-
18
+
19
19
  # Get cached path-based ID for a node by computing its path once
20
20
  def get_node_id(node)
21
21
  return nil unless node.respond_to?(:name) && node.name
22
-
22
+
23
23
  # Compute path for this node (fast with forward traversal counting)
24
- path = compute_path_forward(node)
25
- path
24
+ compute_path_forward(node)
26
25
  end
27
-
26
+
28
27
  private
29
-
28
+
30
29
  def analyze_document
31
30
  # Pre-populate cache is not needed since we compute on demand
32
31
  # The optimization comes from forward-counting siblings instead of backward
33
32
  end
34
-
33
+
35
34
  def compute_path_forward(node)
36
35
  path_parts = []
37
36
  current = node
38
-
37
+
39
38
  while current
40
39
  if current.respond_to?(:name) && current.name
41
40
  # Count position among siblings by iterating forward from parent
42
41
  position = calculate_position_fast(current)
43
42
  path_parts.unshift("#{current.name}[#{position}]")
44
43
  end
45
-
44
+
46
45
  break unless current.respond_to?(:parent)
47
-
46
+
48
47
  begin
49
48
  current = current.parent
50
49
  rescue NoMethodError
51
50
  break
52
51
  end
53
-
52
+
54
53
  break unless current
55
54
  end
56
-
55
+
57
56
  "/#{path_parts.join('/')}"
58
57
  end
59
-
58
+
60
59
  def calculate_position_fast(node)
61
60
  return 1 unless node.respond_to?(:parent)
62
-
61
+
63
62
  parent = begin
64
63
  node.parent
65
64
  rescue NoMethodError
66
65
  nil
67
66
  end
68
-
69
- return 1 unless parent && parent.respond_to?(:children)
70
-
67
+
68
+ return 1 unless parent.respond_to?(:children)
69
+
71
70
  # Count this node's position among siblings with same name
72
71
  position = 0
73
72
  parent.children.each do |child|
74
73
  next unless child.respond_to?(:name) && child.name == node.name
74
+
75
75
  position += 1
76
- break if child.object_id == node.object_id
76
+ break if child.equal?(node)
77
77
  end
78
-
78
+
79
79
  position
80
80
  end
81
81
  end
@@ -39,6 +39,7 @@ module SvgConform
39
39
  node.children.each do |child|
40
40
  # Only process nodes with name (skip text nodes, etc.)
41
41
  next unless child.respond_to?(:name) && child.name
42
+
42
43
  build_index(child, child_counter)
43
44
  end
44
45
  end
@@ -114,19 +114,34 @@ module SvgConform
114
114
  end
115
115
 
116
116
  def validate(document)
117
- context = ValidationContext.new(document, self)
117
+ # Use SAX mode for validation performance
118
+ # Convert document to content string if it's a DOM document
119
+ if document.is_a?(Document)
120
+ content = document.to_xml
121
+ sax_doc = SaxDocument.from_content(content)
122
+ return sax_doc.validate_with_profile(self)
123
+ elsif document.respond_to?(:to_xml)
124
+ # Handle any document-like object
125
+ content = document.to_xml
126
+ sax_doc = SaxDocument.from_content(content)
127
+ return sax_doc.validate_with_profile(self)
128
+ else
129
+ # Fallback to DOM mode for backward compatibility
130
+ context = ValidationContext.new(document, self)
131
+
132
+ # Validate using requirements system
133
+ requirements&.each do |requirement|
134
+ requirement.validate_document(document, context)
135
+ end
118
136
 
119
- # Validate using requirements system
120
- requirements&.each do |requirement|
121
- requirement.validate_document(document, context)
137
+ return ValidationResult.new(document, self, context)
122
138
  end
123
-
124
- ValidationResult.new(document, self, context)
125
139
  end
126
140
 
127
141
  def validate_file(file_path)
128
- document = Document.from_file(file_path)
129
- validate(document)
142
+ # Use SAX mode directly
143
+ sax_doc = SaxDocument.from_file(file_path)
144
+ sax_doc.validate_with_profile(self)
130
145
  end
131
146
 
132
147
  # Apply remediations to a document after validation