svg_conform 0.1.2 → 0.1.3

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +192 -32
  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/requirements/allowed_elements_requirement.rb +29 -9
  8. data/lib/svg_conform/requirements/color_restrictions_requirement.rb +8 -6
  9. data/lib/svg_conform/requirements/font_family_requirement.rb +6 -2
  10. data/lib/svg_conform/requirements/forbidden_content_requirement.rb +2 -2
  11. data/lib/svg_conform/requirements/id_reference_requirement.rb +10 -7
  12. data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +7 -6
  13. data/lib/svg_conform/requirements/link_validation_requirement.rb +2 -2
  14. data/lib/svg_conform/requirements/namespace_attributes_requirement.rb +16 -10
  15. data/lib/svg_conform/requirements/namespace_requirement.rb +11 -10
  16. data/lib/svg_conform/requirements/no_external_css_requirement.rb +4 -4
  17. data/lib/svg_conform/requirements/no_external_fonts_requirement.rb +2 -2
  18. data/lib/svg_conform/requirements/no_external_images_requirement.rb +2 -2
  19. data/lib/svg_conform/requirements/style_promotion_requirement.rb +7 -0
  20. data/lib/svg_conform/requirements/viewbox_required_requirement.rb +7 -7
  21. data/lib/svg_conform/sax_document.rb +2 -2
  22. data/lib/svg_conform/sax_validation_handler.rb +14 -10
  23. data/lib/svg_conform/validation_context.rb +9 -7
  24. data/lib/svg_conform/validator.rb +2 -2
  25. data/lib/svg_conform/version.rb +1 -1
  26. data/spec/spec_helper.rb +3 -0
  27. data/spec/support/shared_examples_for_validation_modes.rb +71 -0
  28. 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: c862af872ab16b243d9f6ffaedb7c5a37e8c470a5280915cc96bc33a3572c8e5
4
+ data.tar.gz: 3b5fbeebd207eb29ac9685a8913799cc8239704566d9efd8b18fa663bf55a8d0
5
5
  SHA512:
6
- metadata.gz: 0ee8ad3bfab5a2aec10b32caa23141967aa25112e0393ed39242ab83a5f123bb317190392df347f7f4a8665af61bf7de4b09eb3283c4c140e372af1177ba924b
7
- data.tar.gz: 20d1871024c131298aa2462bd849e07dfb16b73f2f61913da8f9c2d21b43e87fcfe5ae6ed298971091c0f85734efe8a36790432ccdb18c8878dafc25d238a348
6
+ metadata.gz: d5808529cca60d31e717f907c5fa9cf11753fb7de4bb65416f7a796f007b4a0157f35c28a7c40f7d58a089eb83738ffc16237e60856b6dd54e475db12132ea91
7
+ data.tar.gz: e597d1400a180a30a44ce26437f84bdc4fc8d51d6c7e0e7aa5afdf2adefa8c1c7ad1bd749c613fb9c7635d1fa462993d345866f17775671e5b2b687042778196
data/.rubocop_todo.yml CHANGED
@@ -1,27 +1,66 @@
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 09:35:14 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
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
9
+ # Offense count: 19
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
12
+ # SupportedStyles: with_first_argument, with_fixed_indentation
13
+ Layout/ArgumentAlignment:
14
+ Exclude:
15
+ - 'compare_dom_sax.rb'
16
+ - 'lib/svg_conform/requirements/font_family_requirement.rb'
17
+ - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
18
+ - 'lib/svg_conform/sax_validation_handler.rb'
19
+ - 'spec/support/shared_examples_for_validation_modes.rb'
20
+
21
+ # Offense count: 5
22
+ # This cop supports safe autocorrection (--autocorrect).
23
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
24
+ # SupportedStyles: with_first_element, with_fixed_indentation
25
+ Layout/ArrayAlignment:
26
+ Exclude:
27
+ - 'lib/svg_conform/requirements/color_restrictions_requirement.rb'
28
+ - 'lib/svg_conform/requirements/id_reference_requirement.rb'
29
+ - 'lib/svg_conform/requirements/invalid_id_references_requirement.rb'
30
+
31
+ # Offense count: 5
10
32
  # This cop supports safe autocorrection (--autocorrect).
11
33
  # Configuration parameters: EnforcedStyleAlignWith.
12
34
  # SupportedStylesAlignWith: either, start_of_block, start_of_line
13
35
  Layout/BlockAlignment:
14
36
  Exclude:
37
+ - 'benchmark_bsi_samples.rb'
38
+ - 'check_position_collisions.rb'
39
+ - 'check_unique_positions.rb'
40
+ - 'lib/svg_conform/requirements/allowed_elements_requirement.rb'
15
41
  - 'spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb'
16
42
 
17
- # Offense count: 1
43
+ # Offense count: 8
44
+ # This cop supports safe autocorrection (--autocorrect).
45
+ Layout/BlockEndNewline:
46
+ Exclude:
47
+ - 'benchmark_bsi_samples.rb'
48
+ - 'check_duplicates.rb'
49
+ - 'check_position_collisions.rb'
50
+ - 'check_unique_positions.rb'
51
+ - 'compare_errors.rb'
52
+ - 'find_missing_errors.rb'
53
+ - 'lib/svg_conform/requirements/allowed_elements_requirement.rb'
54
+
55
+ # Offense count: 4
18
56
  # This cop supports safe autocorrection (--autocorrect).
19
57
  # Configuration parameters: AllowForAlignment.
20
58
  Layout/CommentIndentation:
21
59
  Exclude:
60
+ - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
22
61
  - 'lib/svg_conform/validation_context.rb'
23
62
 
24
- # Offense count: 1
63
+ # Offense count: 2
25
64
  # This cop supports safe autocorrection (--autocorrect).
26
65
  Layout/ElseAlignment:
27
66
  Exclude:
@@ -47,17 +86,37 @@ Layout/EmptyLineAfterGuardClause:
47
86
 
48
87
  # Offense count: 1
49
88
  # This cop supports safe autocorrection (--autocorrect).
89
+ # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
90
+ Layout/EmptyLineBetweenDefs:
91
+ Exclude:
92
+ - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
93
+
94
+ # Offense count: 1
95
+ # This cop supports safe autocorrection (--autocorrect).
96
+ Layout/EmptyLines:
97
+ Exclude:
98
+ - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
99
+
100
+ # Offense count: 1
101
+ # This cop supports safe autocorrection (--autocorrect).
102
+ Layout/EmptyLinesAroundMethodBody:
103
+ Exclude:
104
+ - 'lib/svg_conform/fast_document_analyzer.rb'
105
+
106
+ # Offense count: 2
107
+ # This cop supports safe autocorrection (--autocorrect).
50
108
  # Configuration parameters: EnforcedStyleAlignWith, Severity.
51
109
  # SupportedStylesAlignWith: keyword, variable, start_of_line
52
110
  Layout/EndAlignment:
53
111
  Exclude:
54
112
  - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
55
113
 
56
- # Offense count: 21
114
+ # Offense count: 22
57
115
  # This cop supports safe autocorrection (--autocorrect).
58
116
  # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
59
117
  Layout/ExtraSpacing:
60
118
  Exclude:
119
+ - 'benchmark_bsi_samples.rb'
61
120
  - 'find_missing_errors.rb'
62
121
  - 'lib/svg_conform/element_proxy.rb'
63
122
  - 'lib/svg_conform/requirements/id_reference_requirement.rb'
@@ -66,34 +125,61 @@ Layout/ExtraSpacing:
66
125
  - 'lib/svg_conform/validation_context.rb'
67
126
  - 'lib/svg_conform/validator.rb'
68
127
 
69
- # Offense count: 1
128
+ # Offense count: 2
129
+ # This cop supports safe autocorrection (--autocorrect).
130
+ # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
131
+ # SupportedHashRocketStyles: key, separator, table
132
+ # SupportedColonStyles: key, separator, table
133
+ # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
134
+ Layout/HashAlignment:
135
+ Exclude:
136
+ - 'benchmark_bsi_samples.rb'
137
+ - 'final_demo.rb'
138
+
139
+ # Offense count: 2
70
140
  # This cop supports safe autocorrection (--autocorrect).
71
141
  Layout/HeredocIndentation:
72
142
  Exclude:
73
143
  - 'test_sax_basic.rb'
74
144
 
75
- # Offense count: 1
145
+ # Offense count: 8
76
146
  # This cop supports safe autocorrection (--autocorrect).
77
147
  # Configuration parameters: EnforcedStyle.
78
148
  # SupportedStyles: normal, indented_internal_methods
79
149
  Layout/IndentationConsistency:
80
150
  Exclude:
151
+ - 'check_duplicates.rb'
152
+ - 'compare_dom_sax.rb'
153
+ - 'compare_errors.rb'
154
+ - 'find_missing_errors.rb'
81
155
  - 'lib/svg_conform/validation_context.rb'
82
156
 
83
- # Offense count: 1
157
+ # Offense count: 13
84
158
  # This cop supports safe autocorrection (--autocorrect).
85
159
  # Configuration parameters: Width, AllowedPatterns.
86
160
  Layout/IndentationWidth:
87
161
  Exclude:
162
+ - 'benchmark_bsi_samples.rb'
163
+ - 'check_position_collisions.rb'
164
+ - 'check_unique_positions.rb'
165
+ - 'lib/svg_conform/requirements/allowed_elements_requirement.rb'
88
166
  - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
89
167
 
90
- # Offense count: 604
168
+ # Offense count: 640
91
169
  # This cop supports safe autocorrection (--autocorrect).
92
170
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
93
171
  # URISchemes: http, https
94
172
  Layout/LineLength:
95
173
  Enabled: false
96
174
 
175
+ # Offense count: 3
176
+ # This cop supports safe autocorrection (--autocorrect).
177
+ Layout/MultilineBlockLayout:
178
+ Exclude:
179
+ - 'check_duplicates.rb'
180
+ - 'compare_errors.rb'
181
+ - 'find_missing_errors.rb'
182
+
97
183
  # Offense count: 28
98
184
  # This cop supports safe autocorrection (--autocorrect).
99
185
  # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
@@ -109,20 +195,19 @@ Layout/SpaceAroundOperators:
109
195
  - 'final_demo.rb'
110
196
  - 'test_remediation.rb'
111
197
 
112
- # Offense count: 32
198
+ # Offense count: 1
199
+ # This cop supports safe autocorrection (--autocorrect).
200
+ # Configuration parameters: EnforcedStyle.
201
+ # SupportedStyles: final_newline, final_blank_line
202
+ Layout/TrailingEmptyLines:
203
+ Exclude:
204
+ - 'spec/support/shared_examples_for_validation_modes.rb'
205
+
206
+ # Offense count: 67
113
207
  # This cop supports safe autocorrection (--autocorrect).
114
208
  # Configuration parameters: AllowInHeredoc.
115
209
  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'
210
+ Enabled: false
126
211
 
127
212
  # Offense count: 2
128
213
  # Configuration parameters: AllowedMethods.
@@ -169,12 +254,20 @@ Lint/MissingSuper:
169
254
  - 'lib/svg_conform/sax_validation_handler.rb'
170
255
 
171
256
  # Offense count: 3
257
+ # This cop supports unsafe autocorrection (--autocorrect-all).
258
+ # Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods.
259
+ # AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal?
260
+ # AdditionalNilMethods: present?, blank?, try, try!
261
+ Lint/RedundantSafeNavigation:
262
+ Exclude:
263
+ - 'count_elements.rb'
264
+ - 'lib/svg_conform/fast_document_analyzer.rb'
265
+
266
+ # Offense count: 1
172
267
  # This cop supports safe autocorrection (--autocorrect).
173
- Lint/ScriptPermission:
268
+ Lint/RedundantStringCoercion:
174
269
  Exclude:
175
- - 'benchmark_validation.rb'
176
- - 'final_demo.rb'
177
- - 'test_remediation.rb'
270
+ - 'analyze_error_nodes.rb'
178
271
 
179
272
  # Offense count: 1
180
273
  Lint/UnreachableCode:
@@ -204,7 +297,7 @@ Lint/UselessAccessModifier:
204
297
  Metrics/AbcSize:
205
298
  Enabled: false
206
299
 
207
- # Offense count: 21
300
+ # Offense count: 22
208
301
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
209
302
  # AllowedMethods: refine
210
303
  Metrics/BlockLength:
@@ -215,12 +308,12 @@ Metrics/BlockLength:
215
308
  Metrics/BlockNesting:
216
309
  Max: 4
217
310
 
218
- # Offense count: 125
311
+ # Offense count: 126
219
312
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
220
313
  Metrics/CyclomaticComplexity:
221
314
  Enabled: false
222
315
 
223
- # Offense count: 257
316
+ # Offense count: 258
224
317
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
225
318
  Metrics/MethodLength:
226
319
  Max: 154
@@ -273,7 +366,7 @@ RSpec/DescribeClass:
273
366
  - 'spec/svg_conform/profiles/svg_1_2_rfc_with_rdf_profile_spec.rb'
274
367
  - 'spec/svgcheck_compatibility_spec.rb'
275
368
 
276
- # Offense count: 106
369
+ # Offense count: 109
277
370
  # Configuration parameters: CountAsOne.
278
371
  RSpec/ExampleLength:
279
372
  Max: 53
@@ -288,7 +381,7 @@ RSpec/MultipleDescribes:
288
381
  Exclude:
289
382
  - 'spec/svg_conform/batch_report_spec.rb'
290
383
 
291
- # Offense count: 71
384
+ # Offense count: 73
292
385
  RSpec/MultipleExpectations:
293
386
  Max: 6
294
387
 
@@ -297,6 +390,23 @@ Rake/MethodDefinitionInTask:
297
390
  Exclude:
298
391
  - 'lib/tasks/svgcheck.rake'
299
392
 
393
+ # Offense count: 9
394
+ # This cop supports safe autocorrection (--autocorrect).
395
+ # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
396
+ # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
397
+ # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
398
+ # FunctionalMethods: let, let!, subject, watch
399
+ # AllowedMethods: lambda, proc, it
400
+ Style/BlockDelimiters:
401
+ Exclude:
402
+ - 'benchmark_bsi_samples.rb'
403
+ - 'check_duplicates.rb'
404
+ - 'check_position_collisions.rb'
405
+ - 'check_unique_positions.rb'
406
+ - 'compare_errors.rb'
407
+ - 'find_missing_errors.rb'
408
+ - 'lib/svg_conform/requirements/allowed_elements_requirement.rb'
409
+
300
410
  # Offense count: 3
301
411
  # This cop supports unsafe autocorrection (--autocorrect-all).
302
412
  Style/CombinableLoops:
@@ -326,7 +436,29 @@ Style/MissingRespondToMissing:
326
436
  Exclude:
327
437
  - 'lib/svg_conform/cli.rb'
328
438
 
329
- # Offense count: 6
439
+ # Offense count: 3
440
+ # This cop supports safe autocorrection (--autocorrect).
441
+ Style/MultilineIfModifier:
442
+ Exclude:
443
+ - 'benchmark_bsi_samples.rb'
444
+ - 'lib/svg_conform/sax_validation_handler.rb'
445
+
446
+ # Offense count: 1
447
+ # This cop supports safe autocorrection (--autocorrect).
448
+ Style/MultilineTernaryOperator:
449
+ Exclude:
450
+ - 'lib/svg_conform/requirements/namespace_attributes_requirement.rb'
451
+
452
+ # Offense count: 2
453
+ # This cop supports unsafe autocorrection (--autocorrect-all).
454
+ # Configuration parameters: EnforcedStyle.
455
+ # SupportedStyles: literals, strict
456
+ Style/MutableConstant:
457
+ Exclude:
458
+ - 'benchmark_bsi_samples.rb'
459
+ - 'test_sax_on_slow_files.rb'
460
+
461
+ # Offense count: 8
330
462
  # This cop supports unsafe autocorrection (--autocorrect-all).
331
463
  # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
332
464
  # SupportedStyles: predicate, comparison
@@ -335,6 +467,7 @@ Style/NumericPredicate:
335
467
  - 'spec/**/*'
336
468
  - 'benchmark_validation.rb'
337
469
  - 'check_unique_positions.rb'
470
+ - 'compare_dom_sax.rb'
338
471
  - 'lib/svg_conform/validation_context.rb'
339
472
  - 'test_remediation.rb'
340
473
  - 'test_sax_basic.rb'
@@ -366,6 +499,12 @@ Style/RedundantInterpolation:
366
499
  Exclude:
367
500
  - 'analyze_error_nodes.rb'
368
501
 
502
+ # Offense count: 1
503
+ # This cop supports safe autocorrection (--autocorrect).
504
+ Style/RedundantParentheses:
505
+ Exclude:
506
+ - 'compare_dom_sax.rb'
507
+
369
508
  # Offense count: 2
370
509
  # This cop supports safe autocorrection (--autocorrect).
371
510
  # Configuration parameters: EnforcedStyle.
@@ -384,23 +523,36 @@ Style/SafeNavigation:
384
523
  - 'count_elements.rb'
385
524
  - 'lib/svg_conform/fast_document_analyzer.rb'
386
525
 
387
- # Offense count: 4
526
+ # Offense count: 7
388
527
  # This cop supports unsafe autocorrection (--autocorrect-all).
389
528
  # Configuration parameters: Mode.
390
529
  Style/StringConcatenation:
391
530
  Exclude:
531
+ - 'benchmark_bsi_samples.rb'
392
532
  - 'benchmark_sax.rb'
393
533
  - 'benchmark_validation.rb'
394
534
  - 'final_demo.rb'
395
535
  - 'test_remediation.rb'
396
536
 
397
- # Offense count: 78
537
+ # Offense count: 105
398
538
  # This cop supports safe autocorrection (--autocorrect).
399
539
  # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
400
540
  # SupportedStyles: single_quotes, double_quotes
401
541
  Style/StringLiterals:
402
542
  Enabled: false
403
543
 
544
+ # Offense count: 5
545
+ # This cop supports safe autocorrection (--autocorrect).
546
+ # Configuration parameters: EnforcedStyle.
547
+ # SupportedStyles: single_quotes, double_quotes
548
+ Style/StringLiteralsInInterpolation:
549
+ Exclude:
550
+ - 'benchmark_bsi_samples.rb'
551
+ - 'benchmark_sax.rb'
552
+ - 'benchmark_validation.rb'
553
+ - 'final_demo.rb'
554
+ - 'test_remediation.rb'
555
+
404
556
  # Offense count: 34
405
557
  # This cop supports safe autocorrection (--autocorrect).
406
558
  # Configuration parameters: EnforcedStyleForMultiline.
@@ -423,6 +575,14 @@ Style/TrailingCommaInArguments:
423
575
  - 'lib/svg_conform/sax_validation_handler.rb'
424
576
  - 'lib/svg_conform/validator.rb'
425
577
 
578
+ # Offense count: 1
579
+ # This cop supports safe autocorrection (--autocorrect).
580
+ # Configuration parameters: EnforcedStyleForMultiline.
581
+ # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
582
+ Style/TrailingCommaInArrayLiteral:
583
+ Exclude:
584
+ - 'test_sax_on_slow_files.rb'
585
+
426
586
  # Offense count: 12
427
587
  # This cop supports safe autocorrection (--autocorrect).
428
588
  # Configuration parameters: EnforcedStyleForMultiline.
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