svg_conform 0.1.0
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 +7 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +23 -0
- data/.github/workflows/svgcheck-compatibility.yml +135 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.rubocop_todo.yml +183 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +14 -0
- data/README.adoc +1384 -0
- data/Rakefile +15 -0
- data/config/profiles/base.yml +34 -0
- data/config/profiles/lucid_fix.yml +37 -0
- data/config/profiles/metanorma.yml +212 -0
- data/config/profiles/no_external_css.yml +20 -0
- data/config/profiles/svg_1_2_rfc.yml +284 -0
- data/config/profiles/svg_1_2_rfc_with_rdf.yml +145 -0
- data/config/svgcheck_mapping.yml +180 -0
- data/docs/profiles.adoc +547 -0
- data/docs/rdf_metadata_support.adoc +212 -0
- data/docs/remediation.adoc +732 -0
- data/docs/requirements.adoc +709 -0
- data/examples/demo.rb +116 -0
- data/examples/requirements_demo.rb +240 -0
- data/exe/svg_conform +7 -0
- data/lib/svg_conform/batch_report.rb +70 -0
- data/lib/svg_conform/cli.rb +107 -0
- data/lib/svg_conform/commands/check.rb +390 -0
- data/lib/svg_conform/commands/profiles.rb +118 -0
- data/lib/svg_conform/commands/svgcheck.rb +90 -0
- data/lib/svg_conform/commands/svgcheck_compare.rb +92 -0
- data/lib/svg_conform/commands/svgcheck_compatibility.rb +43 -0
- data/lib/svg_conform/commands/svgcheck_generate.rb +262 -0
- data/lib/svg_conform/compatibility/analysis_context.rb +68 -0
- data/lib/svg_conform/compatibility/comparison_result.rb +147 -0
- data/lib/svg_conform/compatibility/compatibility_analyzer.rb +85 -0
- data/lib/svg_conform/compatibility/file_processor.rb +109 -0
- data/lib/svg_conform/compatibility/pattern_discovery.rb +319 -0
- data/lib/svg_conform/compatibility/report_formatter.rb +359 -0
- data/lib/svg_conform/compatibility/svg_analysis_engine.rb +316 -0
- data/lib/svg_conform/compatibility/validity_analysis.rb +252 -0
- data/lib/svg_conform/compatibility/xml_analysis_engine.rb +198 -0
- data/lib/svg_conform/compatibility_analyzer.rb +285 -0
- data/lib/svg_conform/conformance_report.rb +267 -0
- data/lib/svg_conform/constants.rb +199 -0
- data/lib/svg_conform/css_color.rb +262 -0
- data/lib/svg_conform/document.rb +203 -0
- data/lib/svg_conform/external_checkers/svgcheck/compatibility_engine.rb +166 -0
- data/lib/svg_conform/external_checkers/svgcheck/output_generator.rb +101 -0
- data/lib/svg_conform/external_checkers/svgcheck/parser.rb +200 -0
- data/lib/svg_conform/external_checkers/svgcheck/report_comparator.rb +175 -0
- data/lib/svg_conform/external_checkers/svgcheck/report_generator.rb +82 -0
- data/lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb +249 -0
- data/lib/svg_conform/external_checkers/svgcheck.rb +56 -0
- data/lib/svg_conform/external_checkers.rb +34 -0
- data/lib/svg_conform/fixer.rb +56 -0
- data/lib/svg_conform/profile.rb +164 -0
- data/lib/svg_conform/profiles.rb +60 -0
- data/lib/svg_conform/remediation_engine.rb +92 -0
- data/lib/svg_conform/remediation_result.rb +36 -0
- data/lib/svg_conform/remediation_runner.rb +225 -0
- data/lib/svg_conform/remediations/base_remediation.rb +165 -0
- data/lib/svg_conform/remediations/color_remediation.rb +226 -0
- data/lib/svg_conform/remediations/font_embedding_remediation.rb +145 -0
- data/lib/svg_conform/remediations/font_remediation.rb +122 -0
- data/lib/svg_conform/remediations/image_embedding_remediation.rb +154 -0
- data/lib/svg_conform/remediations/invalid_id_references_remediation.rb +129 -0
- data/lib/svg_conform/remediations/namespace_attribute_remediation.rb +244 -0
- data/lib/svg_conform/remediations/namespace_remediation.rb +151 -0
- data/lib/svg_conform/remediations/no_external_css_remediation.rb +192 -0
- data/lib/svg_conform/remediations/style_promotion_remediation.rb +93 -0
- data/lib/svg_conform/remediations/viewbox_remediation.rb +127 -0
- data/lib/svg_conform/remediations.rb +40 -0
- data/lib/svg_conform/report_comparator.rb +772 -0
- data/lib/svg_conform/requirements/allowed_elements_requirement.rb +367 -0
- data/lib/svg_conform/requirements/base_requirement.rb +98 -0
- data/lib/svg_conform/requirements/color_restrictions_requirement.rb +126 -0
- data/lib/svg_conform/requirements/element_requirement_config.rb +75 -0
- data/lib/svg_conform/requirements/font_family_requirement.rb +133 -0
- data/lib/svg_conform/requirements/forbidden_content_requirement.rb +60 -0
- data/lib/svg_conform/requirements/id_reference_requirement.rb +133 -0
- data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +132 -0
- data/lib/svg_conform/requirements/link_validation_requirement.rb +55 -0
- data/lib/svg_conform/requirements/namespace_attributes_requirement.rb +211 -0
- data/lib/svg_conform/requirements/namespace_requirement.rb +294 -0
- data/lib/svg_conform/requirements/no_external_css_requirement.rb +132 -0
- data/lib/svg_conform/requirements/no_external_fonts_requirement.rb +121 -0
- data/lib/svg_conform/requirements/no_external_images_requirement.rb +91 -0
- data/lib/svg_conform/requirements/style_promotion_requirement.rb +72 -0
- data/lib/svg_conform/requirements/style_requirement.rb +226 -0
- data/lib/svg_conform/requirements/viewbox_required_requirement.rb +96 -0
- data/lib/svg_conform/requirements.rb +49 -0
- data/lib/svg_conform/semantic_comparator.rb +829 -0
- data/lib/svg_conform/validation_context.rb +408 -0
- data/lib/svg_conform/validation_result.rb +146 -0
- data/lib/svg_conform/validator.rb +91 -0
- data/lib/svg_conform/version.rb +5 -0
- data/lib/svg_conform.rb +68 -0
- data/lib/tasks/fixtures.rake +321 -0
- data/lib/tasks/svgcheck.rake +111 -0
- data/reference-docs/SVG-1.2-RFC.rnc.txt +1676 -0
- data/reference-docs/Scalable Vector Graphics (SVG) 1.1 (Second Edition).html +40764 -0
- data/reference-docs/Scalable Vector Graphics (SVG) Tiny 1.2 Specification.html +44591 -0
- data/reference-docs/rfc7996.txt +2971 -0
- data/sig/svg_conform.rbs +4 -0
- data/spec/fixtures/allowed_elements/inputs/basic_violations.svg +21 -0
- data/spec/fixtures/allowed_elements/repair/basic_violations.svg +18 -0
- data/spec/fixtures/color_restrictions/inputs/basic_violations.svg +23 -0
- data/spec/fixtures/color_restrictions/repair/basic_violations.svg +23 -0
- data/spec/fixtures/comprehensive/inputs/multiple_violations.svg +21 -0
- data/spec/fixtures/comprehensive/repair/multiple_violations.svg +16 -0
- data/spec/fixtures/font_family/inputs/basic_violations.svg +17 -0
- data/spec/fixtures/font_family/repair/basic_violations.svg +17 -0
- data/spec/fixtures/forbidden_content/inputs/basic_violations.svg +27 -0
- data/spec/fixtures/forbidden_content/repair/basic_violations.svg +19 -0
- data/spec/fixtures/id_reference/inputs/basic_violations.svg +17 -0
- data/spec/fixtures/id_reference/repair/basic_violations.svg +15 -0
- data/spec/fixtures/link_validation/inputs/basic_violations.svg +20 -0
- data/spec/fixtures/link_validation/repair/basic_violations.svg +16 -0
- data/spec/fixtures/lucid/inputs/simple.svg +67 -0
- data/spec/fixtures/lucid/repair/simple.svg +63 -0
- data/spec/fixtures/namespace/inputs/basic_violations.svg +20 -0
- data/spec/fixtures/namespace/repair/basic_violations.svg +17 -0
- data/spec/fixtures/namespace_attributes/inputs/basic_violations.svg +16 -0
- data/spec/fixtures/namespace_attributes/repair/basic_violations.svg +15 -0
- data/spec/fixtures/no_external_css/inputs/basic_violations.svg +20 -0
- data/spec/fixtures/no_external_css/repair/basic_violations.svg +18 -0
- data/spec/fixtures/style/inputs/basic_violations.svg +22 -0
- data/spec/fixtures/style/repair/basic_violations.svg +22 -0
- data/spec/fixtures/style_promotion/inputs/basic_test.svg +15 -0
- data/spec/fixtures/style_promotion/repair/basic_test.svg +15 -0
- data/spec/fixtures/svg_1_2_rfc/inputs/allowed_elements_violations.svg +18 -0
- data/spec/fixtures/svg_1_2_rfc/inputs/color_restrictions_violations.svg +23 -0
- data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.out +23 -0
- data/spec/fixtures/svgcheck/check/IETF-test.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/IETF-test.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/IETF-test.svg.out +20 -0
- data/spec/fixtures/svgcheck/check/circle.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/circle.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/circle.svg.out +2 -0
- data/spec/fixtures/svgcheck/check/colors.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/colors.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/colors.svg.out +13 -0
- data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.out +76 -0
- data/spec/fixtures/svgcheck/check/example-dot.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/example-dot.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/example-dot.svg.out +11 -0
- data/spec/fixtures/svgcheck/check/full-tiny.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/full-tiny.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/full-tiny.svg.out +5835 -0
- data/spec/fixtures/svgcheck/check/good.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/good.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/good.svg.out +1 -0
- data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.out +5 -0
- data/spec/fixtures/svgcheck/check/malformed.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/malformed.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/malformed.svg.out +8 -0
- data/spec/fixtures/svgcheck/check/rfc-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/rfc-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/rfc-svg.svg.out +1 -0
- data/spec/fixtures/svgcheck/check/rfc.xml.code +1 -0
- data/spec/fixtures/svgcheck/check/rfc.xml.err +0 -0
- data/spec/fixtures/svgcheck/check/rfc.xml.out +2 -0
- data/spec/fixtures/svgcheck/check/rgb.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/rgb.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/rgb.svg.out +9 -0
- data/spec/fixtures/svgcheck/check/svg-wordle.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/svg-wordle.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/svg-wordle.svg.out +508 -0
- data/spec/fixtures/svgcheck/check/threshold.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/threshold.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/threshold.svg.out +20 -0
- data/spec/fixtures/svgcheck/check/utf8.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/utf8.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/utf8.svg.out +162 -0
- data/spec/fixtures/svgcheck/check/viewBox-both.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-both.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-both.svg.out +4 -0
- data/spec/fixtures/svgcheck/check/viewBox-height.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-height.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-height.svg.out +3 -0
- data/spec/fixtures/svgcheck/check/viewBox-none.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-none.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-none.svg.out +3 -0
- data/spec/fixtures/svgcheck/check/viewBox-width.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-width.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-width.svg.out +3 -0
- data/spec/fixtures/svgcheck/inputs/DrawBerry-sample-2.svg +28 -0
- data/spec/fixtures/svgcheck/inputs/IETF-test.svg +28 -0
- data/spec/fixtures/svgcheck/inputs/circle.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/colors.svg +18 -0
- data/spec/fixtures/svgcheck/inputs/dia-sample-svg.svg +47 -0
- data/spec/fixtures/svgcheck/inputs/example-dot.svg +75 -0
- data/spec/fixtures/svgcheck/inputs/full-tiny.svg +16194 -0
- data/spec/fixtures/svgcheck/inputs/good.svg +19 -0
- data/spec/fixtures/svgcheck/inputs/httpbis-proxy20-fig6.svg +2 -0
- data/spec/fixtures/svgcheck/inputs/malformed.svg +11 -0
- data/spec/fixtures/svgcheck/inputs/rfc-svg.svg +1028 -0
- data/spec/fixtures/svgcheck/inputs/rfc.xml +37 -0
- data/spec/fixtures/svgcheck/inputs/rgb.svg +9 -0
- data/spec/fixtures/svgcheck/inputs/svg-wordle.svg +330 -0
- data/spec/fixtures/svgcheck/inputs/threshold.svg +26 -0
- data/spec/fixtures/svgcheck/inputs/utf8.svg +448 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-both.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-height.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-none.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-width.svg +3 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.file +0 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.out +23 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.file +29 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.out +20 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.out +2 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.file +19 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.out +13 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.file +47 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.out +76 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.file +69 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.out +11 -0
- data/spec/fixtures/svgcheck/repair/good.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/good.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/good.svg.file +0 -0
- data/spec/fixtures/svgcheck/repair/good.svg.out +1 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.file +73 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.out +5 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.file +9 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.out +8 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.file +0 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.out +1 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.code +1 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.err +0 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.file +37 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.out +2 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.file +10 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.out +9 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.file +112 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.out +508 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.file +27 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.out +20 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.file +381 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.out +162 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.out +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.out +3 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.out +3 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.out +3 -0
- data/spec/fixtures/viewbox_required/inputs/missing_viewbox.svg +10 -0
- data/spec/fixtures/viewbox_required/repair/missing_viewbox.svg +10 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/svg_conform/batch_report_spec.rb +99 -0
- data/spec/svg_conform/commands/check_command_spec.rb +90 -0
- data/spec/svg_conform/commands/profiles_command_spec.rb +20 -0
- data/spec/svg_conform/commands/svgcheck_compare_command_spec.rb +13 -0
- data/spec/svg_conform/commands/svgcheck_compatibility_command_spec.rb +13 -0
- data/spec/svg_conform/commands/svgcheck_generate_command_spec.rb +14 -0
- data/spec/svg_conform/profiles/base_profile_spec.rb +42 -0
- data/spec/svg_conform/profiles/lucid_fix_profile_spec.rb +46 -0
- data/spec/svg_conform/profiles/lucid_profile_spec.rb +84 -0
- data/spec/svg_conform/profiles/metanorma_profile_spec.rb +62 -0
- data/spec/svg_conform/profiles/no_external_css_profile_spec.rb +66 -0
- data/spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb +200 -0
- data/spec/svg_conform/profiles/svg_1_2_rfc_with_rdf_profile_spec.rb +81 -0
- data/spec/svg_conform/remediations/color_remediation_spec.rb +95 -0
- data/spec/svg_conform/remediations/font_embedding_remediation_spec.rb +20 -0
- data/spec/svg_conform/remediations/font_remediation_spec.rb +95 -0
- data/spec/svg_conform/remediations/image_embedding_remediation_spec.rb +20 -0
- data/spec/svg_conform/remediations/invalid_id_references_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/namespace_attribute_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/namespace_remediation_spec.rb +95 -0
- data/spec/svg_conform/remediations/no_external_css_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/style_promotion_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/viewbox_remediation_spec.rb +95 -0
- data/spec/svg_conform/requirements/allowed_elements_requirement_spec.rb +118 -0
- data/spec/svg_conform/requirements/color_restrictions_requirement_spec.rb +168 -0
- data/spec/svg_conform/requirements/font_family_requirement_spec.rb +188 -0
- data/spec/svg_conform/requirements/forbidden_content_requirement_spec.rb +195 -0
- data/spec/svg_conform/requirements/id_reference_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/invalid_id_references_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/link_validation_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/namespace_attributes_requirement_spec.rb +86 -0
- data/spec/svg_conform/requirements/namespace_requirement_spec.rb +184 -0
- data/spec/svg_conform/requirements/no_external_css_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/no_external_fonts_requirement_spec.rb +20 -0
- data/spec/svg_conform/requirements/no_external_images_requirement_spec.rb +20 -0
- data/spec/svg_conform/requirements/style_promotion_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/style_requirement_spec.rb +76 -0
- data/spec/svg_conform/requirements/viewbox_required_requirement_spec.rb +165 -0
- data/spec/svg_conform_spec.rb +32 -0
- data/spec/svgcheck_compatibility_spec.rb +355 -0
- data/svg_conform.gemspec +35 -0
- metadata +436 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
5
|
+
module SvgConform
|
|
6
|
+
# Context object passed to rules during validation
|
|
7
|
+
class ValidationContext
|
|
8
|
+
attr_reader :document, :profile, :errors, :warnings, :fixes,
|
|
9
|
+
:validity_errors
|
|
10
|
+
|
|
11
|
+
def initialize(document, profile)
|
|
12
|
+
@document = document
|
|
13
|
+
@profile = profile
|
|
14
|
+
@errors = []
|
|
15
|
+
@warnings = []
|
|
16
|
+
@validity_errors = []
|
|
17
|
+
@infos = []
|
|
18
|
+
@data = {}
|
|
19
|
+
@structurally_invalid_node_ids = Set.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Mark a node as structurally invalid (e.g., invalid parent-child relationship)
|
|
23
|
+
# Other requirements should skip attribute validation on these nodes
|
|
24
|
+
# Also marks all descendants as invalid since they'll be removed with the parent
|
|
25
|
+
def mark_node_structurally_invalid(node)
|
|
26
|
+
node_id = generate_node_id(node)
|
|
27
|
+
@structurally_invalid_node_ids.add(node_id)
|
|
28
|
+
|
|
29
|
+
# Mark all descendants as invalid too
|
|
30
|
+
mark_descendants_invalid(node)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Mark all descendants of a node as structurally invalid
|
|
34
|
+
def mark_descendants_invalid(node)
|
|
35
|
+
return unless node.respond_to?(:children)
|
|
36
|
+
|
|
37
|
+
node.children.each do |child|
|
|
38
|
+
child_id = generate_node_id(child)
|
|
39
|
+
@structurally_invalid_node_ids.add(child_id)
|
|
40
|
+
# Recursively mark descendants
|
|
41
|
+
mark_descendants_invalid(child)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if a node is structurally invalid
|
|
46
|
+
def node_structurally_invalid?(node)
|
|
47
|
+
node_id = generate_node_id(node)
|
|
48
|
+
@structurally_invalid_node_ids.include?(node_id)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def add_error(node:, message:, rule: nil, requirement: nil,
|
|
52
|
+
requirement_id: nil, severity: nil, fix: nil, data: {})
|
|
53
|
+
# Support both old rule system and new requirements system
|
|
54
|
+
rule_or_requirement = requirement || rule
|
|
55
|
+
|
|
56
|
+
error = ValidationIssue.new(
|
|
57
|
+
type: :error,
|
|
58
|
+
rule: rule_or_requirement,
|
|
59
|
+
node: node,
|
|
60
|
+
message: message,
|
|
61
|
+
fix: fix,
|
|
62
|
+
data: data,
|
|
63
|
+
requirement_id: requirement_id,
|
|
64
|
+
severity: severity,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Handle special severity types
|
|
68
|
+
if severity == :validity_error
|
|
69
|
+
@validity_errors << error
|
|
70
|
+
else
|
|
71
|
+
@errors << error
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
error
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def add_warning(rule:, node:, message:, fix: nil)
|
|
78
|
+
warning = ValidationIssue.new(
|
|
79
|
+
type: :warning,
|
|
80
|
+
rule: rule,
|
|
81
|
+
node: node,
|
|
82
|
+
message: message,
|
|
83
|
+
fix: fix,
|
|
84
|
+
)
|
|
85
|
+
@warnings << warning
|
|
86
|
+
warning
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def add_fix(fix)
|
|
90
|
+
@fixes << fix
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def has_errors?
|
|
94
|
+
!@errors.empty?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def has_warnings?
|
|
98
|
+
!@warnings.empty?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def has_fixes?
|
|
102
|
+
!@fixes.empty?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def issue_count
|
|
106
|
+
@errors.size + @warnings.size
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def fixable_count
|
|
110
|
+
(@errors + @warnings).count(&:fixable?)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def set_data(key, value)
|
|
114
|
+
@data[key] = value
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def get_data(key)
|
|
118
|
+
@data[key]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Generate a unique identifier for a node based on its path
|
|
122
|
+
# Builds a stable path by walking up the parent chain
|
|
123
|
+
def generate_node_id(node)
|
|
124
|
+
return nil unless node.respond_to?(:name)
|
|
125
|
+
|
|
126
|
+
# Build path by walking up parent chain
|
|
127
|
+
path_parts = []
|
|
128
|
+
current = node
|
|
129
|
+
|
|
130
|
+
while current
|
|
131
|
+
if current.respond_to?(:name) && current.name
|
|
132
|
+
# Count previous siblings of the same type for position
|
|
133
|
+
position = 1
|
|
134
|
+
if current.respond_to?(:previous_sibling)
|
|
135
|
+
sibling = current.previous_sibling
|
|
136
|
+
while sibling
|
|
137
|
+
position += 1 if sibling.respond_to?(:name) && sibling.name == current.name
|
|
138
|
+
sibling = sibling.previous_sibling if sibling.respond_to?(:previous_sibling)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
path_parts.unshift("#{current.name}[#{position}]")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Stop if we reach the document root (doesn't have parent)
|
|
146
|
+
break unless current.respond_to?(:parent)
|
|
147
|
+
|
|
148
|
+
begin
|
|
149
|
+
current = current.parent
|
|
150
|
+
rescue NoMethodError
|
|
151
|
+
# Parent method failed, we're at root
|
|
152
|
+
break
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
break unless current
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
"/#{path_parts.join('/')}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Base class for validation issues
|
|
163
|
+
class ValidationIssue
|
|
164
|
+
attr_reader :type, :rule, :node, :message, :fix, :data,
|
|
165
|
+
:requirement_id_override, :severity
|
|
166
|
+
|
|
167
|
+
def initialize(type:, rule:, node:, message:, fix: nil, data: {},
|
|
168
|
+
requirement_id: nil, severity: nil, violation_type: nil)
|
|
169
|
+
@type = type
|
|
170
|
+
@rule = rule
|
|
171
|
+
@node = node
|
|
172
|
+
@message = message
|
|
173
|
+
@fix = fix
|
|
174
|
+
@data = data
|
|
175
|
+
@requirement_id_override = requirement_id
|
|
176
|
+
@severity = severity
|
|
177
|
+
@violation_type = violation_type || detect_violation_type
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def requirement_id
|
|
181
|
+
return @requirement_id_override.to_s if @requirement_id_override
|
|
182
|
+
|
|
183
|
+
if @rule.respond_to?(:id)
|
|
184
|
+
@rule.id.to_s
|
|
185
|
+
elsif @rule.respond_to?(:class) && @rule.class.respond_to?(:name)
|
|
186
|
+
# Extract ID from class name for requirements
|
|
187
|
+
class_name = @rule.class.name.split("::").last
|
|
188
|
+
class_name.gsub(/Requirement$/, "").downcase.gsub(/([a-z])([A-Z])/,
|
|
189
|
+
'\1_\2').downcase
|
|
190
|
+
else
|
|
191
|
+
"unknown"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def error?
|
|
196
|
+
@type == :error
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def warning?
|
|
200
|
+
@type == :warning
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def fixable?
|
|
204
|
+
!@fix.nil?
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def line
|
|
208
|
+
@node.respond_to?(:line) ? @node.line : nil
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def column
|
|
212
|
+
@node.respond_to?(:column) ? @node.column : nil
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def element_name
|
|
216
|
+
@node.respond_to?(:name) ? @node.name : nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def apply_fix
|
|
220
|
+
return false unless fixable?
|
|
221
|
+
|
|
222
|
+
begin
|
|
223
|
+
if @fix.respond_to?(:call)
|
|
224
|
+
@fix.call
|
|
225
|
+
else
|
|
226
|
+
@fix.apply
|
|
227
|
+
end
|
|
228
|
+
true
|
|
229
|
+
rescue StandardError
|
|
230
|
+
false
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def to_h
|
|
235
|
+
{
|
|
236
|
+
type: @type,
|
|
237
|
+
rule: rule_id,
|
|
238
|
+
message: @message,
|
|
239
|
+
line: line,
|
|
240
|
+
column: column,
|
|
241
|
+
element: element_name,
|
|
242
|
+
fixable: fixable?,
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def violation_type
|
|
247
|
+
@violation_type
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def remediable?
|
|
251
|
+
case @violation_type
|
|
252
|
+
when :color_violation, :font_violation, :content_violation, :reference_violation,
|
|
253
|
+
:namespace_violation, :viewbox_violation, :style_violation
|
|
254
|
+
true
|
|
255
|
+
when :structural_violation
|
|
256
|
+
false
|
|
257
|
+
else
|
|
258
|
+
# Default to remediable for backward compatibility
|
|
259
|
+
true
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def remediation_type
|
|
264
|
+
case @violation_type
|
|
265
|
+
when :color_violation, :font_violation
|
|
266
|
+
:convert
|
|
267
|
+
when :content_violation, :reference_violation, :namespace_violation
|
|
268
|
+
:remove
|
|
269
|
+
when :viewbox_violation
|
|
270
|
+
:add
|
|
271
|
+
when :style_violation
|
|
272
|
+
:promote
|
|
273
|
+
else
|
|
274
|
+
:unknown
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def remediation_confidence
|
|
279
|
+
case @violation_type
|
|
280
|
+
when :color_violation, :font_violation, :style_violation
|
|
281
|
+
:automatic
|
|
282
|
+
when :viewbox_violation, :namespace_violation
|
|
283
|
+
:safe_structural
|
|
284
|
+
when :content_violation, :reference_violation
|
|
285
|
+
:safe_removal
|
|
286
|
+
else
|
|
287
|
+
:manual_review
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def suggested_action
|
|
292
|
+
case @violation_type
|
|
293
|
+
when :color_violation
|
|
294
|
+
"Convert color to allowed equivalent using CssColor"
|
|
295
|
+
when :font_violation
|
|
296
|
+
"Map font family to generic equivalent"
|
|
297
|
+
when :content_violation
|
|
298
|
+
"Remove forbidden element or attribute"
|
|
299
|
+
when :reference_violation
|
|
300
|
+
"Remove broken reference or containing element"
|
|
301
|
+
when :namespace_violation
|
|
302
|
+
"Fix namespace declarations and remove invalid elements/attributes"
|
|
303
|
+
when :viewbox_violation
|
|
304
|
+
"Add missing viewBox attribute"
|
|
305
|
+
when :style_violation
|
|
306
|
+
"Promote style properties to attributes"
|
|
307
|
+
when :structural_violation
|
|
308
|
+
"Manual fix required - document structure issue"
|
|
309
|
+
else
|
|
310
|
+
"Apply available remediation"
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def affects_content?
|
|
315
|
+
case @violation_type
|
|
316
|
+
when :content_violation, :reference_violation
|
|
317
|
+
true
|
|
318
|
+
when :color_violation, :font_violation, :style_violation, :viewbox_violation, :namespace_violation
|
|
319
|
+
false
|
|
320
|
+
else
|
|
321
|
+
false
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def to_s
|
|
326
|
+
location = line ? " at line #{line}" : ""
|
|
327
|
+
location += ":#{column}" if column
|
|
328
|
+
rule_info = rule_id ? " (#{rule_id})" : ""
|
|
329
|
+
remediation_info = remediable? ? " [#{remediation_type}]" : " [NOT REMEDIABLE]"
|
|
330
|
+
"#{@message}#{location}#{rule_info}#{remediation_info}"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def to_h
|
|
334
|
+
{
|
|
335
|
+
type: @type,
|
|
336
|
+
rule: rule_id,
|
|
337
|
+
message: @message,
|
|
338
|
+
line: line,
|
|
339
|
+
column: column,
|
|
340
|
+
element: element_name,
|
|
341
|
+
fixable: fixable?,
|
|
342
|
+
remediable: remediable?,
|
|
343
|
+
violation_type: @violation_type,
|
|
344
|
+
remediation_type: remediation_type,
|
|
345
|
+
remediation_confidence: remediation_confidence,
|
|
346
|
+
suggested_action: suggested_action,
|
|
347
|
+
affects_content: affects_content?,
|
|
348
|
+
}
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
private
|
|
352
|
+
|
|
353
|
+
def detect_violation_type
|
|
354
|
+
req_id = requirement_id.downcase
|
|
355
|
+
msg = @message.downcase
|
|
356
|
+
|
|
357
|
+
# First check for structural violations (non-remediable)
|
|
358
|
+
return :structural_violation if msg.include?("root element must be") ||
|
|
359
|
+
msg.include?("malformed") ||
|
|
360
|
+
msg.include?("invalid document") ||
|
|
361
|
+
msg.include?("required element missing") ||
|
|
362
|
+
msg.include?("invalid hierarchy")
|
|
363
|
+
|
|
364
|
+
# Then check requirement-based violations (remediable)
|
|
365
|
+
case req_id
|
|
366
|
+
when /color/
|
|
367
|
+
:color_violation
|
|
368
|
+
when /font/
|
|
369
|
+
:font_violation
|
|
370
|
+
when /forbidden|content/
|
|
371
|
+
:content_violation
|
|
372
|
+
when /reference|id/
|
|
373
|
+
:reference_violation
|
|
374
|
+
when /namespace/
|
|
375
|
+
:namespace_violation
|
|
376
|
+
when /viewbox/
|
|
377
|
+
:viewbox_violation
|
|
378
|
+
when /style/
|
|
379
|
+
:style_violation
|
|
380
|
+
else
|
|
381
|
+
# Check message content for clues
|
|
382
|
+
return :color_violation if msg.include?("color")
|
|
383
|
+
return :font_violation if msg.include?("font")
|
|
384
|
+
return :content_violation if msg.include?("forbidden")
|
|
385
|
+
return :reference_violation if msg.include?("reference") || msg.include?("href")
|
|
386
|
+
return :namespace_violation if msg.include?("namespace")
|
|
387
|
+
return :viewbox_violation if msg.include?("viewbox")
|
|
388
|
+
return :style_violation if msg.include?("style")
|
|
389
|
+
|
|
390
|
+
:unknown_violation
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def rule_id
|
|
395
|
+
return @requirement_id_override if @requirement_id_override
|
|
396
|
+
return @rule.id if @rule.respond_to?(:id)
|
|
397
|
+
|
|
398
|
+
if @rule.respond_to?(:class) && @rule.class.respond_to?(:name)
|
|
399
|
+
# Extract ID from class name for requirements
|
|
400
|
+
class_name = @rule.class.name.split("::").last
|
|
401
|
+
class_name.gsub(/Requirement$/, "").downcase.gsub(/([a-z])([A-Z])/,
|
|
402
|
+
'\1_\2').downcase
|
|
403
|
+
else
|
|
404
|
+
"unknown"
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module SvgConform
|
|
6
|
+
# Represents the result of validating an SVG document
|
|
7
|
+
class ValidationResult
|
|
8
|
+
attr_reader :document, :profile, :errors, :warnings, :validity_errors,
|
|
9
|
+
:fixes_applied, :fixed_document
|
|
10
|
+
|
|
11
|
+
def initialize(document, profile, context)
|
|
12
|
+
@document = document
|
|
13
|
+
@profile = profile
|
|
14
|
+
@errors = context.errors
|
|
15
|
+
@warnings = context.warnings
|
|
16
|
+
@validity_errors = context.validity_errors
|
|
17
|
+
@fixes_applied = []
|
|
18
|
+
@fixed_document = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def valid?
|
|
22
|
+
@errors.empty? && @validity_errors.empty?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def has_errors?
|
|
26
|
+
!@errors.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def has_warnings?
|
|
30
|
+
!@warnings.empty?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fixable?
|
|
34
|
+
(@errors + @warnings).any?(&:fixable?)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def fixed?
|
|
38
|
+
!@fixed_document.nil?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def error_count
|
|
42
|
+
@errors.size
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def warning_count
|
|
46
|
+
@warnings.size
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def issue_count
|
|
50
|
+
error_count + warning_count
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def fixable_count
|
|
54
|
+
(@errors + @warnings).count(&:fixable?)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def failed_requirements
|
|
58
|
+
@errors.select(&:requirement_id)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def apply_fixes
|
|
62
|
+
return self unless fixable?
|
|
63
|
+
|
|
64
|
+
# Create a copy of the document for fixing
|
|
65
|
+
@fixed_document = @document.dup
|
|
66
|
+
@fixes_applied = []
|
|
67
|
+
|
|
68
|
+
# Apply fixes for errors first, then warnings
|
|
69
|
+
fixable_issues = (@errors + @warnings).select(&:fixable?)
|
|
70
|
+
|
|
71
|
+
fixable_issues.each do |issue|
|
|
72
|
+
@fixes_applied << issue if issue.apply_fix
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_s(format: :text)
|
|
79
|
+
case format
|
|
80
|
+
when :text
|
|
81
|
+
to_text
|
|
82
|
+
when :json
|
|
83
|
+
to_json
|
|
84
|
+
when :hash
|
|
85
|
+
to_h
|
|
86
|
+
else
|
|
87
|
+
to_text
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def to_h
|
|
92
|
+
{
|
|
93
|
+
file: @document.file_path,
|
|
94
|
+
profile: @profile.name,
|
|
95
|
+
valid: valid?,
|
|
96
|
+
errors: @errors.map(&:to_h),
|
|
97
|
+
warnings: @warnings.map(&:to_h),
|
|
98
|
+
fixes_applied: @fixes_applied.size,
|
|
99
|
+
fixable: fixable_count,
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def to_json(*args)
|
|
104
|
+
JSON.pretty_generate(to_h, *args)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def to_text
|
|
110
|
+
lines = []
|
|
111
|
+
lines << "SVG Conformance Report"
|
|
112
|
+
lines << "=" * 22
|
|
113
|
+
lines << ""
|
|
114
|
+
lines << "File: #{@document.file_path || 'inline'}"
|
|
115
|
+
lines << "Profile: #{@profile.name} (#{@profile.description})"
|
|
116
|
+
lines << "Status: #{valid? ? 'VALID' : 'INVALID'}"
|
|
117
|
+
lines << ""
|
|
118
|
+
|
|
119
|
+
if has_errors?
|
|
120
|
+
lines << "Errors (#{error_count}):"
|
|
121
|
+
@errors.each_with_index do |error, i|
|
|
122
|
+
lines << " #{i + 1}. #{error}"
|
|
123
|
+
end
|
|
124
|
+
lines << ""
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if has_warnings?
|
|
128
|
+
lines << "Warnings (#{warning_count}):"
|
|
129
|
+
@warnings.each_with_index do |warning, i|
|
|
130
|
+
lines << " #{i + 1}. #{warning}"
|
|
131
|
+
end
|
|
132
|
+
lines << ""
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if fixable?
|
|
136
|
+
lines << "Fixes Available: #{fixable_count}"
|
|
137
|
+
lines << "Fixes Applied: #{@fixes_applied.size}" if fixed?
|
|
138
|
+
lines << ""
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
lines << "✓ Document is valid and conforms to the #{@profile.name} profile." if valid? && !has_warnings?
|
|
142
|
+
|
|
143
|
+
lines.join("\n")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SvgConform
|
|
4
|
+
# Main validator class for SVG conformance checking
|
|
5
|
+
class Validator
|
|
6
|
+
attr_reader :options
|
|
7
|
+
|
|
8
|
+
def initialize(options = {})
|
|
9
|
+
@options = {
|
|
10
|
+
fix: false,
|
|
11
|
+
strict: false,
|
|
12
|
+
}.merge(options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Validate an SVG file
|
|
16
|
+
def validate_file(file_path, profile: :svg_1_2_rfc, **options)
|
|
17
|
+
unless File.exist?(file_path)
|
|
18
|
+
raise ValidationError,
|
|
19
|
+
"File not found: #{file_path}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
document = Document.from_file(file_path)
|
|
23
|
+
validate_document(document, profile: profile, **options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Validate SVG content string
|
|
27
|
+
def validate(svg_content, profile: :svg_1_2_rfc, **options)
|
|
28
|
+
document = Document.from_content(svg_content)
|
|
29
|
+
validate_document(document, profile: profile, **options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Validate a Document object
|
|
33
|
+
def validate_document(document, profile: :svg_1_2_rfc, **options)
|
|
34
|
+
merged_options = @options.merge(options)
|
|
35
|
+
profile_obj = resolve_profile(profile)
|
|
36
|
+
|
|
37
|
+
# Perform validation
|
|
38
|
+
result = profile_obj.validate(document)
|
|
39
|
+
|
|
40
|
+
# Apply fixes if requested
|
|
41
|
+
result.apply_fixes if merged_options[:fix] && result.fixable?
|
|
42
|
+
|
|
43
|
+
result
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Validate multiple files
|
|
47
|
+
def validate_files(file_paths, profile: :svg_1_2_rfc, **options)
|
|
48
|
+
results = {}
|
|
49
|
+
|
|
50
|
+
file_paths.each do |file_path|
|
|
51
|
+
results[file_path] =
|
|
52
|
+
validate_file(file_path, profile: profile, **options)
|
|
53
|
+
rescue StandardError => e
|
|
54
|
+
results[file_path] = create_error_result(file_path, e)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
results
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get available profiles
|
|
61
|
+
def available_profiles
|
|
62
|
+
SvgConform::Profiles.available_profiles
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def resolve_profile(profile)
|
|
68
|
+
case profile
|
|
69
|
+
when Symbol, String
|
|
70
|
+
SvgConform::Profiles.get(profile) || raise(ProfileError,
|
|
71
|
+
"Unknown profile: #{profile}")
|
|
72
|
+
when Profile
|
|
73
|
+
profile
|
|
74
|
+
else
|
|
75
|
+
raise ProfileError, "Invalid profile type: #{profile.class}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def create_error_result(file_path, error)
|
|
80
|
+
# Create a minimal error result for files that couldn't be processed
|
|
81
|
+
OpenStruct.new(
|
|
82
|
+
valid?: false,
|
|
83
|
+
errors: [error],
|
|
84
|
+
warnings: [],
|
|
85
|
+
file_path: file_path,
|
|
86
|
+
error?: true,
|
|
87
|
+
to_s: -> { "Error processing #{file_path}: #{error.message}" },
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/svg_conform.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
# Configure Lutaml::Model adapters
|
|
6
|
+
Lutaml::Model::Config.configure do |config|
|
|
7
|
+
config.xml_adapter_type = :nokogiri
|
|
8
|
+
config.json_adapter_type = :standard_json
|
|
9
|
+
config.yaml_adapter_type = :standard_yaml
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require_relative "svg_conform/version"
|
|
13
|
+
require_relative "svg_conform/document"
|
|
14
|
+
require_relative "svg_conform/validation_context"
|
|
15
|
+
require_relative "svg_conform/validation_result"
|
|
16
|
+
require_relative "svg_conform/profiles"
|
|
17
|
+
require_relative "svg_conform/requirements"
|
|
18
|
+
require_relative "svg_conform/remediations"
|
|
19
|
+
require_relative "svg_conform/remediation_result"
|
|
20
|
+
require_relative "svg_conform/external_checkers"
|
|
21
|
+
require_relative "svg_conform/cli"
|
|
22
|
+
|
|
23
|
+
module SvgConform
|
|
24
|
+
class Error < StandardError; end
|
|
25
|
+
class ParseError < Error; end
|
|
26
|
+
class ValidationError < Error; end
|
|
27
|
+
class ProfileError < Error; end
|
|
28
|
+
class RuleError < Error; end
|
|
29
|
+
|
|
30
|
+
# Autoload core classes
|
|
31
|
+
autoload :Validator, File.expand_path("svg_conform/validator", __dir__)
|
|
32
|
+
autoload :Fixer, "svg_conform/fixer"
|
|
33
|
+
autoload :RemediationEngine, "svg_conform/remediation_engine"
|
|
34
|
+
autoload :ConformanceReport,
|
|
35
|
+
File.expand_path("svg_conform/conformance_report", __dir__)
|
|
36
|
+
autoload :BatchReport,
|
|
37
|
+
File.expand_path("svg_conform/batch_report", __dir__)
|
|
38
|
+
autoload :ReportComparator,
|
|
39
|
+
File.expand_path("svg_conform/report_comparator", __dir__)
|
|
40
|
+
autoload :NamespaceRemediation, "svg_conform/namespace_remediation"
|
|
41
|
+
autoload :Cli, "svg_conform/cli"
|
|
42
|
+
|
|
43
|
+
# Autoload command classes
|
|
44
|
+
module Commands
|
|
45
|
+
autoload :Check, "svg_conform/commands/check"
|
|
46
|
+
autoload :Compare, "svg_conform/commands/compare"
|
|
47
|
+
autoload :Compatibility,
|
|
48
|
+
File.expand_path("svg_conform/commands/compatibility", __dir__)
|
|
49
|
+
autoload :GenerateReports,
|
|
50
|
+
File.expand_path("svg_conform/commands/generate_reports", __dir__)
|
|
51
|
+
autoload :Profiles, "svg_conform/commands/profiles"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Convenience method to create a new validator
|
|
55
|
+
def self.validator
|
|
56
|
+
Validator.new
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Validate a file with the specified profile
|
|
60
|
+
def self.validate_file(file_path, profile: :svg_1_2_rfc, **options)
|
|
61
|
+
validator.validate_file(file_path, profile: profile, **options)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Validate SVG content with the specified profile
|
|
65
|
+
def self.validate(svg_content, profile: :svg_1_2_rfc, **options)
|
|
66
|
+
validator.validate(svg_content, profile: profile, **options)
|
|
67
|
+
end
|
|
68
|
+
end
|