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,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SvgConform
|
|
4
|
+
module ExternalCheckers
|
|
5
|
+
module Svgcheck
|
|
6
|
+
# Compatibility engine for matching svg_conform behavior to svgcheck
|
|
7
|
+
class CompatibilityEngine
|
|
8
|
+
def initialize
|
|
9
|
+
# Load any compatibility-specific configuration
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Check if file should be treated as unparseable by svgcheck
|
|
13
|
+
def should_mimic_parse_failure?(_filename, _validation_result)
|
|
14
|
+
# Implement parse failure detection logic based on svgcheck behavior
|
|
15
|
+
# For now, return false - this would need to be enhanced based on actual svgcheck behavior
|
|
16
|
+
false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check if validity errors should be included as regular errors (for svgcheck compatibility)
|
|
20
|
+
def should_include_validity_errors?(validation_result, filename)
|
|
21
|
+
# Include validity errors if both width and height are present
|
|
22
|
+
# This matches svgcheck's behavior from the mapping config
|
|
23
|
+
return false unless validation_result.validity_errors.any?
|
|
24
|
+
|
|
25
|
+
# Check for specific datatype condition mentioned in mapping config
|
|
26
|
+
has_width_and_height_datatype_condition?(validation_result, filename)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Filter errors for svgcheck compatibility
|
|
30
|
+
def filter_errors_for_svgcheck(errors, _filename, _validation_result)
|
|
31
|
+
filtered_errors = []
|
|
32
|
+
|
|
33
|
+
errors.each do |error|
|
|
34
|
+
# Skip namespace_validation errors only for RDF/metadata namespaces
|
|
35
|
+
# svgcheck skips these silently, but reports other namespace errors
|
|
36
|
+
# Only skip RDF-related namespace errors
|
|
37
|
+
if (error.requirement_id == "namespace_validation") && rdf_namespace_error?(error)
|
|
38
|
+
next
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Map svg_conform requirement IDs to svgcheck-compatible ones
|
|
42
|
+
mapped_req_id = map_requirement_id(error.requirement_id)
|
|
43
|
+
next if mapped_req_id.nil? # Skip unmapped requirements
|
|
44
|
+
|
|
45
|
+
filtered_errors << [error, mapped_req_id]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
filtered_errors
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def rdf_namespace_error?(error)
|
|
54
|
+
return false unless error.respond_to?(:message)
|
|
55
|
+
|
|
56
|
+
# List of RDF/metadata namespaces that svgcheck silently skips
|
|
57
|
+
rdf_namespaces = [
|
|
58
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
|
59
|
+
"http://creativecommons.org/ns#",
|
|
60
|
+
"http://purl.org/dc/elements/1.1/",
|
|
61
|
+
"http://purl.org/dc/dcmitype/",
|
|
62
|
+
"http://www.w3.org/2000/01/rdf-schema#",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
rdf_namespaces.any? { |ns| error.message.include?(ns) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def has_width_and_height_datatype_condition?(validation_result,
|
|
69
|
+
_filename)
|
|
70
|
+
# This would need to be implemented based on the specific logic
|
|
71
|
+
# mentioned in the mapping config's "datatype_condition": "both_width_and_height_present"
|
|
72
|
+
# For now, return true if there are validity errors
|
|
73
|
+
validation_result.validity_errors.any?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def map_requirement_id(requirement_id)
|
|
77
|
+
# Map svg_conform requirement IDs to svgcheck-compatible requirement IDs
|
|
78
|
+
mapping = {
|
|
79
|
+
"allowed_elements" => "allowed_elements",
|
|
80
|
+
# forbidden_content not mapped - it's redundant with allowed_elements checks
|
|
81
|
+
"namespace_attributes" => "namespace_attributes",
|
|
82
|
+
"namespace_validation" => "namespace_attributes", # Map to closest equivalent
|
|
83
|
+
"font_family" => "style", # svgcheck reports font-family issues as style errors
|
|
84
|
+
"color_restrictions" => "color_restrictions",
|
|
85
|
+
"viewbox_required" => "viewbox_required",
|
|
86
|
+
"style" => "style", # Direct mapping for StyleRequirement
|
|
87
|
+
"style_promotion" => "style",
|
|
88
|
+
"style_syntax" => "style",
|
|
89
|
+
"style_validation" => "style",
|
|
90
|
+
"property_value" => "style",
|
|
91
|
+
"required_attribute" => "viewbox_required",
|
|
92
|
+
"namespace" => "namespace_attributes",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Return nil for unmapped requirements (like invalid_id_references)
|
|
96
|
+
mapping[requirement_id.to_s]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Extract semantic meaning from error for comparison
|
|
100
|
+
def extract_semantic_meaning(error)
|
|
101
|
+
# Ensure we have a valid error object
|
|
102
|
+
return "unknown" unless error.respond_to?(:message) && error.respond_to?(:requirement_id)
|
|
103
|
+
|
|
104
|
+
# Try to create a semantic key based on the error type and context
|
|
105
|
+
requirement = error.requirement_id
|
|
106
|
+
message = error.message
|
|
107
|
+
|
|
108
|
+
# Handle different types of errors semantically
|
|
109
|
+
case requirement
|
|
110
|
+
when "color_restrictions"
|
|
111
|
+
# Extract color and attribute for semantic grouping
|
|
112
|
+
if message&.include?("doesn't allow") && (match = message.match(/attribute ['"]([^'"]+)['"].*value ['"]([^'"]+)['"]/))
|
|
113
|
+
attribute = match[1]
|
|
114
|
+
value = match[2]
|
|
115
|
+
"color_restriction:#{attribute}:#{value}"
|
|
116
|
+
else
|
|
117
|
+
requirement
|
|
118
|
+
end
|
|
119
|
+
when "font_family"
|
|
120
|
+
# Group font family errors by attribute and font
|
|
121
|
+
if message&.include?("font-family") && (match = message.match(/font-family.*['"]([^'"]+)['"]/))
|
|
122
|
+
font = match[1]
|
|
123
|
+
"font_restriction:#{font}"
|
|
124
|
+
else
|
|
125
|
+
requirement
|
|
126
|
+
end
|
|
127
|
+
when "allowed_elements"
|
|
128
|
+
# Group by element type
|
|
129
|
+
if message&.include?("not allowed") && (match = message.match(/Element ['"]([^'"]+)['"]/))
|
|
130
|
+
element = match[1]
|
|
131
|
+
"element_not_allowed:#{element}"
|
|
132
|
+
elsif message&.include?("does not allow the attribute") && (match = message.match(/element ['"]([^'"]+)['"].*attribute ['"]([^'"]+)['"]/))
|
|
133
|
+
element = match[1]
|
|
134
|
+
attribute = match[2]
|
|
135
|
+
"invalid_attribute:#{element}:#{attribute}"
|
|
136
|
+
else
|
|
137
|
+
requirement
|
|
138
|
+
end
|
|
139
|
+
when "namespace_attributes"
|
|
140
|
+
# Group by namespace issues
|
|
141
|
+
if message&.include?("namespace") && (match = message.match(/namespace ['"]([^'"]+)['"]/))
|
|
142
|
+
namespace = match[1]
|
|
143
|
+
"namespace_issue:#{namespace}"
|
|
144
|
+
else
|
|
145
|
+
requirement
|
|
146
|
+
end
|
|
147
|
+
when "viewbox_required"
|
|
148
|
+
# ViewBox is generally consistent
|
|
149
|
+
"viewbox_required"
|
|
150
|
+
when "style"
|
|
151
|
+
# Group style issues by property
|
|
152
|
+
if message&.include?("style") && (match = message.match(/property ['"]([^'"]+)['"]/))
|
|
153
|
+
property = match[1]
|
|
154
|
+
"style_issue:#{property}"
|
|
155
|
+
else
|
|
156
|
+
requirement
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
# Fallback to requirement_id for other cases
|
|
160
|
+
requirement || "unknown"
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module SvgConform
|
|
7
|
+
module ExternalCheckers
|
|
8
|
+
module Svgcheck
|
|
9
|
+
# Generates svgcheck outputs for comparison testing
|
|
10
|
+
class OutputGenerator
|
|
11
|
+
attr_reader :svgcheck_exec, :svgcheck_path
|
|
12
|
+
|
|
13
|
+
def initialize(svgcheck_exec: "python3",
|
|
14
|
+
svgcheck_path: "svgcheck/svgcheck")
|
|
15
|
+
@svgcheck_exec = svgcheck_exec
|
|
16
|
+
@svgcheck_path = svgcheck_path
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Generate svgcheck outputs for a given input file
|
|
20
|
+
def generate(input_file, mode: :both)
|
|
21
|
+
results = {}
|
|
22
|
+
|
|
23
|
+
case mode
|
|
24
|
+
when :check
|
|
25
|
+
results[:check] = generate_mode_output(input_file, :check)
|
|
26
|
+
when :repair
|
|
27
|
+
results[:repair] = generate_mode_output(input_file, :repair)
|
|
28
|
+
when :both
|
|
29
|
+
results[:check] = generate_mode_output(input_file, :check)
|
|
30
|
+
results[:repair] = generate_mode_output(input_file, :repair)
|
|
31
|
+
else
|
|
32
|
+
raise ArgumentError,
|
|
33
|
+
"Invalid mode: #{mode}. Must be :check, :repair, or :both"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
results
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def generate_mode_output(input_file, mode)
|
|
42
|
+
unless available?
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: "svgcheck not available (#{@svgcheck_exec} or #{@svgcheck_path} not found)",
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
# Change to svgcheck directory to run the module
|
|
51
|
+
original_dir = Dir.pwd
|
|
52
|
+
Dir.chdir(@svgcheck_path)
|
|
53
|
+
|
|
54
|
+
# Build command based on mode
|
|
55
|
+
relative_input = File.join("Tests", File.basename(input_file))
|
|
56
|
+
cmd = if mode == :repair
|
|
57
|
+
[@svgcheck_exec, "run.py", "--repair", relative_input]
|
|
58
|
+
else
|
|
59
|
+
[@svgcheck_exec, "run.py", relative_input] # check mode - no --repair flag
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
|
63
|
+
|
|
64
|
+
# Return to original directory
|
|
65
|
+
Dir.chdir(original_dir)
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
success: true,
|
|
69
|
+
stdout: stdout,
|
|
70
|
+
stderr: stderr,
|
|
71
|
+
exit_code: status.exitstatus,
|
|
72
|
+
mode: mode,
|
|
73
|
+
}
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
# Make sure we return to original directory even on error
|
|
76
|
+
Dir.chdir(original_dir) if Dir.pwd != original_dir
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
success: false,
|
|
80
|
+
error: "Error running svgcheck in #{mode} mode: #{e.message}",
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def available?
|
|
86
|
+
command_available?(@svgcheck_exec) && Dir.exist?(@svgcheck_path)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# SECURITY: Prevent command injection by using array form of system()
|
|
90
|
+
# GitHub CodeQL: Uncontrolled command line (CWE-078)
|
|
91
|
+
# This affects svgcheck comparison commands (development use only)
|
|
92
|
+
def command_available?(command)
|
|
93
|
+
# Use array form to prevent shell injection
|
|
94
|
+
system("which", command, out: File::NULL, err: File::NULL) ||
|
|
95
|
+
system("where", command, out: File::NULL, err: File::NULL) || # Windows
|
|
96
|
+
File.executable?(command) # Direct path check
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module SvgConform
|
|
6
|
+
module ExternalCheckers
|
|
7
|
+
module Svgcheck
|
|
8
|
+
# Enhanced svgcheck output parser that uses mapping configuration
|
|
9
|
+
class Parser
|
|
10
|
+
attr_reader :mapping_config
|
|
11
|
+
|
|
12
|
+
def initialize(mapping_config_path = nil)
|
|
13
|
+
@mapping_config_path = mapping_config_path || "config/svgcheck_mapping.yml"
|
|
14
|
+
load_mapping_config
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Parse svgcheck output into a ConformanceReport
|
|
18
|
+
def parse(output_content, _error_content = nil, filename: nil)
|
|
19
|
+
return create_empty_report(filename) if output_content.nil? || output_content.strip.empty?
|
|
20
|
+
|
|
21
|
+
errors = parse_error_lines(output_content)
|
|
22
|
+
create_report_from_errors(errors, filename)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def load_mapping_config
|
|
28
|
+
@mapping_config = if File.exist?(@mapping_config_path)
|
|
29
|
+
YAML.load_file(@mapping_config_path)
|
|
30
|
+
else
|
|
31
|
+
# Fallback to empty config if file doesn't exist
|
|
32
|
+
{
|
|
33
|
+
"message_patterns" => [],
|
|
34
|
+
"requirement_mappings" => {},
|
|
35
|
+
"skip_patterns" => [],
|
|
36
|
+
"special_rules" => {},
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def parse_error_lines(content)
|
|
42
|
+
errors = []
|
|
43
|
+
|
|
44
|
+
content.each_line do |line|
|
|
45
|
+
line = line.strip
|
|
46
|
+
next if line.empty?
|
|
47
|
+
next if should_skip_line?(line)
|
|
48
|
+
|
|
49
|
+
# Try to match against message patterns
|
|
50
|
+
matched_error = match_against_patterns(line)
|
|
51
|
+
errors << if matched_error
|
|
52
|
+
matched_error
|
|
53
|
+
else
|
|
54
|
+
# Fallback to basic error structure for unmapped errors
|
|
55
|
+
create_unmapped_error(line)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
errors
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def should_skip_line?(line)
|
|
63
|
+
return false unless @mapping_config["skip_patterns"]
|
|
64
|
+
|
|
65
|
+
@mapping_config["skip_patterns"].each do |skip_pattern|
|
|
66
|
+
pattern = skip_pattern["pattern"]
|
|
67
|
+
flags = skip_pattern["flags"]
|
|
68
|
+
|
|
69
|
+
regex_flags = 0
|
|
70
|
+
regex_flags |= Regexp::IGNORECASE if flags&.include?("i")
|
|
71
|
+
|
|
72
|
+
if line.match?(Regexp.new(pattern, regex_flags))
|
|
73
|
+
return true
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def match_against_patterns(line)
|
|
81
|
+
return nil unless @mapping_config["message_patterns"]
|
|
82
|
+
|
|
83
|
+
@mapping_config["message_patterns"].each do |pattern_config|
|
|
84
|
+
pattern = pattern_config["pattern"]
|
|
85
|
+
requirement = pattern_config["requirement"]
|
|
86
|
+
semantic_key = pattern_config["semantic_key"]
|
|
87
|
+
|
|
88
|
+
match = line.match(Regexp.new(pattern))
|
|
89
|
+
next unless match
|
|
90
|
+
|
|
91
|
+
# Skip patterns that map to null requirement (unmapped errors)
|
|
92
|
+
return nil if requirement.nil?
|
|
93
|
+
|
|
94
|
+
return create_mapped_error(line, requirement, semantic_key, match)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def create_mapped_error(line, requirement_id, semantic_key, match)
|
|
101
|
+
# Substitute regex groups in semantic key
|
|
102
|
+
processed_semantic_key = semantic_key
|
|
103
|
+
if semantic_key && match.captures.any?
|
|
104
|
+
match.captures.each_with_index do |capture, index|
|
|
105
|
+
processed_semantic_key = processed_semantic_key.gsub(
|
|
106
|
+
"${#{index + 1}}", capture || ""
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Apply requirement_mappings to normalize requirement IDs
|
|
112
|
+
normalized_requirement_id = apply_requirement_mapping(requirement_id)
|
|
113
|
+
|
|
114
|
+
issue = SvgConform::ConformanceIssue.new
|
|
115
|
+
issue.type = "error"
|
|
116
|
+
issue.requirement_id = normalized_requirement_id
|
|
117
|
+
issue.message = line
|
|
118
|
+
|
|
119
|
+
# Extract semantic information from the match
|
|
120
|
+
if (match.captures.length >= 1) && match.captures[0]
|
|
121
|
+
issue.attribute = match.captures[0]
|
|
122
|
+
end
|
|
123
|
+
if (match.captures.length >= 2) && match.captures[1]
|
|
124
|
+
issue.value = match.captures[1]
|
|
125
|
+
end
|
|
126
|
+
if (match.captures.length >= 3) && match.captures[2]
|
|
127
|
+
issue.element = match.captures[2]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
issue
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def apply_requirement_mapping(requirement_id)
|
|
134
|
+
return requirement_id unless @mapping_config["requirement_mappings"]
|
|
135
|
+
|
|
136
|
+
# Apply requirement_mappings to normalize requirement IDs
|
|
137
|
+
mappings = @mapping_config["requirement_mappings"]
|
|
138
|
+
mappings[requirement_id.to_s] || requirement_id
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def create_unmapped_error(line)
|
|
142
|
+
issue = SvgConform::ConformanceIssue.new
|
|
143
|
+
issue.type = "error"
|
|
144
|
+
issue.requirement_id = "unmapped_svgcheck_error"
|
|
145
|
+
issue.message = line
|
|
146
|
+
issue
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def create_report_from_errors(errors, filename)
|
|
150
|
+
report = SvgConform::ConformanceReport.new
|
|
151
|
+
report.filename = filename
|
|
152
|
+
report.tool = "svgcheck"
|
|
153
|
+
report.timestamp = Time.now.iso8601
|
|
154
|
+
report.valid = errors.empty?
|
|
155
|
+
|
|
156
|
+
# Initialize summaries
|
|
157
|
+
report.errors = SvgConform::IssueSummary.new
|
|
158
|
+
report.errors.total_count = errors.length
|
|
159
|
+
report.errors.by_requirement = {}
|
|
160
|
+
report.errors.issues = errors
|
|
161
|
+
|
|
162
|
+
report.warnings = SvgConform::IssueSummary.new
|
|
163
|
+
report.warnings.total_count = 0
|
|
164
|
+
report.warnings.by_requirement = {}
|
|
165
|
+
report.warnings.issues = []
|
|
166
|
+
|
|
167
|
+
# Group errors by requirement
|
|
168
|
+
if errors.any?
|
|
169
|
+
error_groups = errors.group_by(&:requirement_id)
|
|
170
|
+
error_groups.each do |req_id, req_errors|
|
|
171
|
+
report.errors.by_requirement[req_id] = req_errors.length
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
report
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def create_empty_report(filename)
|
|
179
|
+
report = SvgConform::ConformanceReport.new
|
|
180
|
+
report.filename = filename
|
|
181
|
+
report.tool = "svgcheck"
|
|
182
|
+
report.timestamp = Time.now.iso8601
|
|
183
|
+
report.valid = true
|
|
184
|
+
|
|
185
|
+
report.errors = SvgConform::IssueSummary.new
|
|
186
|
+
report.errors.total_count = 0
|
|
187
|
+
report.errors.by_requirement = {}
|
|
188
|
+
report.errors.issues = []
|
|
189
|
+
|
|
190
|
+
report.warnings = SvgConform::IssueSummary.new
|
|
191
|
+
report.warnings.total_count = 0
|
|
192
|
+
report.warnings.by_requirement = {}
|
|
193
|
+
report.warnings.issues = []
|
|
194
|
+
|
|
195
|
+
report
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../conformance_report"
|
|
4
|
+
require_relative "../../validator"
|
|
5
|
+
require_relative "../../profiles"
|
|
6
|
+
|
|
7
|
+
module SvgConform
|
|
8
|
+
module ExternalCheckers
|
|
9
|
+
module Svgcheck
|
|
10
|
+
# Sophisticated report comparison mechanism for svg_conform vs svgcheck
|
|
11
|
+
class ReportComparator
|
|
12
|
+
attr_reader :test_files_dir, :svgcheck_outputs_dir
|
|
13
|
+
|
|
14
|
+
def initialize(test_files_dir: "svgcheck/svgcheck/Tests",
|
|
15
|
+
svgcheck_outputs_dir: "spec/fixtures/svgcheck")
|
|
16
|
+
@test_files_dir = test_files_dir
|
|
17
|
+
@svgcheck_outputs_dir = svgcheck_outputs_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Compare all test files between svg_conform and svgcheck
|
|
21
|
+
def compare_all_test_files
|
|
22
|
+
test_files = find_test_files
|
|
23
|
+
|
|
24
|
+
results = {
|
|
25
|
+
total_files: test_files.length,
|
|
26
|
+
matching: 0,
|
|
27
|
+
different: 0,
|
|
28
|
+
errors: 0,
|
|
29
|
+
differences: {},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test_files.each do |filename|
|
|
33
|
+
comparison = compare_file(filename)
|
|
34
|
+
|
|
35
|
+
if comparison[:identical]
|
|
36
|
+
results[:matching] += 1
|
|
37
|
+
else
|
|
38
|
+
results[:different] += 1
|
|
39
|
+
results[:differences][filename] = comparison
|
|
40
|
+
end
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
results[:errors] += 1
|
|
43
|
+
results[:differences][filename] = {
|
|
44
|
+
error: e.message,
|
|
45
|
+
summary: "Error during comparison: #{e.message}",
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
results
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Compare a single file between svg_conform and svgcheck
|
|
53
|
+
def compare_file(filename)
|
|
54
|
+
svg_conform_report = generate_svg_conform_report(filename)
|
|
55
|
+
svgcheck_report = load_svgcheck_report(filename)
|
|
56
|
+
|
|
57
|
+
unless svgcheck_report
|
|
58
|
+
return { error: "No svgcheck report found",
|
|
59
|
+
identical: false }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Perform detailed comparison
|
|
63
|
+
comparison = svg_conform_report.compare_with(svgcheck_report)
|
|
64
|
+
|
|
65
|
+
# Add semantic comparison
|
|
66
|
+
semantic_comparison = perform_semantic_comparison(svg_conform_report,
|
|
67
|
+
svgcheck_report)
|
|
68
|
+
comparison[:semantic] = semantic_comparison
|
|
69
|
+
|
|
70
|
+
comparison
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def find_test_files
|
|
76
|
+
return [] unless Dir.exist?(@test_files_dir)
|
|
77
|
+
|
|
78
|
+
pattern = File.join(@test_files_dir, "*.{svg,xml}")
|
|
79
|
+
Dir.glob(pattern).map { |path| File.basename(path) }.sort
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def generate_svg_conform_report(filename)
|
|
83
|
+
input_file = File.join(@test_files_dir, filename)
|
|
84
|
+
return nil unless File.exist?(input_file)
|
|
85
|
+
|
|
86
|
+
# Load svg_1_2_rfc profile
|
|
87
|
+
profile = SvgConform::Profiles.load_profile(:svg_1_2_rfc)
|
|
88
|
+
|
|
89
|
+
# Validate using svg_conform
|
|
90
|
+
validator = SvgConform::Validator.new(profile)
|
|
91
|
+
validation_result = validator.validate_file(input_file)
|
|
92
|
+
|
|
93
|
+
# Create report with svgcheck mapping for compatibility
|
|
94
|
+
ConformanceReport.from_svg_conform_result(
|
|
95
|
+
filename,
|
|
96
|
+
validation_result,
|
|
97
|
+
profile: "svg_1_2_rfc",
|
|
98
|
+
use_svgcheck_mapping: true,
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def load_svgcheck_report(filename)
|
|
103
|
+
base_name = File.basename(filename, ".*")
|
|
104
|
+
extension = File.extname(filename)
|
|
105
|
+
|
|
106
|
+
# Try to load from check mode first, then repair mode
|
|
107
|
+
check_error_file = File.join(@svgcheck_outputs_dir, "check",
|
|
108
|
+
"#{base_name}#{extension}.out")
|
|
109
|
+
repair_error_file = File.join(@svgcheck_outputs_dir, "repair",
|
|
110
|
+
"#{base_name}#{extension}.out")
|
|
111
|
+
|
|
112
|
+
error_file = if File.exist?(check_error_file)
|
|
113
|
+
check_error_file
|
|
114
|
+
elsif File.exist?(repair_error_file)
|
|
115
|
+
repair_error_file
|
|
116
|
+
else
|
|
117
|
+
return nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
error_content = File.read(error_file)
|
|
121
|
+
ConformanceReport.from_svgcheck_result(filename, error_content)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def perform_semantic_comparison(svg_conform_report, svgcheck_report)
|
|
125
|
+
semantic_issues = []
|
|
126
|
+
|
|
127
|
+
# Compare semantic equivalence of errors
|
|
128
|
+
svg_conform_errors = group_errors_semantically(svg_conform_report.errors.issues)
|
|
129
|
+
svgcheck_errors = group_errors_semantically(svgcheck_report.errors.issues)
|
|
130
|
+
|
|
131
|
+
# Find semantic mismatches
|
|
132
|
+
all_semantic_keys = (svg_conform_errors.keys + svgcheck_errors.keys).uniq
|
|
133
|
+
|
|
134
|
+
all_semantic_keys.each do |semantic_key|
|
|
135
|
+
svg_count = svg_conform_errors[semantic_key]&.length || 0
|
|
136
|
+
svgcheck_count = svgcheck_errors[semantic_key]&.length || 0
|
|
137
|
+
|
|
138
|
+
next if svg_count == svgcheck_count
|
|
139
|
+
|
|
140
|
+
semantic_issues << {
|
|
141
|
+
semantic_key: semantic_key,
|
|
142
|
+
svg_conform_count: svg_count,
|
|
143
|
+
svgcheck_count: svgcheck_count,
|
|
144
|
+
difference: svg_count - svgcheck_count,
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
{
|
|
149
|
+
total_semantic_groups: all_semantic_keys.length,
|
|
150
|
+
matching_groups: all_semantic_keys.length - semantic_issues.length,
|
|
151
|
+
mismatched_groups: semantic_issues.length,
|
|
152
|
+
issues: semantic_issues,
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def group_errors_semantically(errors)
|
|
157
|
+
errors.group_by do |error|
|
|
158
|
+
# Create semantic key based on requirement and core issue
|
|
159
|
+
requirement = error.requirement_id
|
|
160
|
+
attribute = error.attribute
|
|
161
|
+
element = error.element
|
|
162
|
+
|
|
163
|
+
if attribute && element
|
|
164
|
+
"#{requirement}:#{element}:#{attribute}"
|
|
165
|
+
elsif element
|
|
166
|
+
"#{requirement}:#{element}"
|
|
167
|
+
else
|
|
168
|
+
requirement
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|