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,390 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "paint"
|
|
4
|
+
require "table_tennis"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require_relative "../batch_report"
|
|
7
|
+
|
|
8
|
+
module SvgConform
|
|
9
|
+
module Commands
|
|
10
|
+
# Check command for validating SVG files (single or batch)
|
|
11
|
+
class Check
|
|
12
|
+
def initialize(files, options)
|
|
13
|
+
@files = Array(files)
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def execute
|
|
18
|
+
# Determine which files to process
|
|
19
|
+
files_to_process = determine_files
|
|
20
|
+
|
|
21
|
+
if files_to_process.empty?
|
|
22
|
+
puts Paint["Error: No SVG files found", :red]
|
|
23
|
+
exit 1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Single file mode vs batch mode
|
|
27
|
+
if files_to_process.length == 1 && !@options[:directory]
|
|
28
|
+
process_single_file(files_to_process.first)
|
|
29
|
+
else
|
|
30
|
+
process_batch(files_to_process)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def determine_files
|
|
37
|
+
if @options[:directory]
|
|
38
|
+
# Directory mode: recursive scan
|
|
39
|
+
dir = @options[:directory]
|
|
40
|
+
unless Dir.exist?(dir)
|
|
41
|
+
puts Paint["Error: Directory '#{dir}' not found", :red]
|
|
42
|
+
exit 1
|
|
43
|
+
end
|
|
44
|
+
Dir.glob(File.join(dir, "**/*.svg")).sort
|
|
45
|
+
elsif @files.empty?
|
|
46
|
+
puts Paint["Error: No files or directory specified", :red]
|
|
47
|
+
exit 1
|
|
48
|
+
[]
|
|
49
|
+
else
|
|
50
|
+
# File mode: validate each file exists
|
|
51
|
+
@files.each do |file|
|
|
52
|
+
unless File.exist?(file)
|
|
53
|
+
puts Paint["Error: File '#{file}' not found", :red]
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
@files
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def process_single_file(file)
|
|
62
|
+
@file = file
|
|
63
|
+
|
|
64
|
+
begin
|
|
65
|
+
# Validate the file
|
|
66
|
+
validator = SvgConform::Validator.new
|
|
67
|
+
result = validator.validate_file(@file,
|
|
68
|
+
profile: @options[:profile].to_sym)
|
|
69
|
+
|
|
70
|
+
# Generate report
|
|
71
|
+
report = SvgConform::ConformanceReport.from_svg_conform_result(
|
|
72
|
+
File.basename(@file),
|
|
73
|
+
result,
|
|
74
|
+
profile: @options[:profile].to_sym,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Output based on format
|
|
78
|
+
case @options[:format]
|
|
79
|
+
when "yaml"
|
|
80
|
+
output_yaml(report)
|
|
81
|
+
when "json"
|
|
82
|
+
output_json(report)
|
|
83
|
+
else
|
|
84
|
+
output_table(report)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Create remediated file if requested
|
|
88
|
+
create_remediated_file(result) if @options[:fix]
|
|
89
|
+
|
|
90
|
+
# Exit with appropriate code
|
|
91
|
+
exit(report.valid? ? 0 : 1)
|
|
92
|
+
rescue StandardError => e
|
|
93
|
+
puts Paint["Error: #{e.message}", :red]
|
|
94
|
+
exit 1
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def output_table(report)
|
|
99
|
+
puts Paint["SVG Validation Report", :bold]
|
|
100
|
+
puts "=" * 50
|
|
101
|
+
puts "File: #{@file}"
|
|
102
|
+
puts "Profile: #{@options[:profile]}"
|
|
103
|
+
puts "Valid: #{report.valid? ? Paint['✓', :green] : Paint['✗', :red]}"
|
|
104
|
+
puts "Total Errors: #{report.errors.total_count}"
|
|
105
|
+
puts
|
|
106
|
+
|
|
107
|
+
if report.errors.total_count.positive?
|
|
108
|
+
puts Paint["Validation Errors:", :bold]
|
|
109
|
+
puts
|
|
110
|
+
|
|
111
|
+
# Group errors by type
|
|
112
|
+
error_groups = report.errors.issues.group_by(&:message)
|
|
113
|
+
|
|
114
|
+
error_groups.each do |message, errors|
|
|
115
|
+
puts Paint["• #{message}", :red]
|
|
116
|
+
if errors.size > 1
|
|
117
|
+
puts Paint[" (#{errors.size} occurrences)",
|
|
118
|
+
:black]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Show first few locations
|
|
122
|
+
errors.first(3).each do |error|
|
|
123
|
+
puts Paint[" at #{error.element}", :black] if error.element
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if errors.size > 3
|
|
127
|
+
puts Paint[" ... and #{errors.size - 3} more",
|
|
128
|
+
:black]
|
|
129
|
+
end
|
|
130
|
+
puts
|
|
131
|
+
end
|
|
132
|
+
else
|
|
133
|
+
puts Paint["✓ No validation errors found", :green]
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def output_yaml(report)
|
|
138
|
+
output = report.to_yaml
|
|
139
|
+
write_output(output)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def output_json(report)
|
|
143
|
+
write_output(report.to_json)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def write_output(content)
|
|
147
|
+
if @options[:output]
|
|
148
|
+
File.write(@options[:output], content)
|
|
149
|
+
puts Paint["Report written to #{@options[:output]}", :green]
|
|
150
|
+
else
|
|
151
|
+
puts content
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def process_batch(files)
|
|
156
|
+
puts Paint["Batch Processing", :bold]
|
|
157
|
+
puts "=" * 80
|
|
158
|
+
puts "Files: #{files.length}"
|
|
159
|
+
puts "Profile: #{@options[:profile]}"
|
|
160
|
+
puts "=" * 80
|
|
161
|
+
puts
|
|
162
|
+
|
|
163
|
+
# Initialize batch report
|
|
164
|
+
batch_report = BatchReport.new
|
|
165
|
+
batch_report.directory = @options[:directory] || File.dirname(files.first)
|
|
166
|
+
batch_report.profile = @options[:profile]
|
|
167
|
+
|
|
168
|
+
# Validate output directory if fixing
|
|
169
|
+
if @options[:fix]
|
|
170
|
+
validate_output_options(files)
|
|
171
|
+
prepare_output_directory
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Process each file
|
|
175
|
+
files.each_with_index do |file, index|
|
|
176
|
+
unless @options[:quiet]
|
|
177
|
+
puts "[#{index + 1}/#{files.length}] #{File.basename(file)}"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
file_result = process_file_for_batch(file)
|
|
181
|
+
batch_report.files << file_result
|
|
182
|
+
|
|
183
|
+
# Update manifest if remediated
|
|
184
|
+
if file_result.remediated_path
|
|
185
|
+
batch_report.manifest[file] = file_result.remediated_path
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Calculate statistics
|
|
190
|
+
batch_report.calculate_statistics
|
|
191
|
+
|
|
192
|
+
# Output results
|
|
193
|
+
output_batch_results(batch_report)
|
|
194
|
+
|
|
195
|
+
# Create manifest file if requested or if fixing
|
|
196
|
+
create_manifest_file(batch_report) if @options[:manifest] || @options[:fix]
|
|
197
|
+
|
|
198
|
+
# Exit with appropriate code
|
|
199
|
+
exit(batch_report.failed.zero? ? 0 : 1)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def process_file_for_batch(file)
|
|
203
|
+
file_result = FileResult.new
|
|
204
|
+
file_result.filename = File.basename(file)
|
|
205
|
+
file_result.original_path = file
|
|
206
|
+
|
|
207
|
+
begin
|
|
208
|
+
# Initial validation
|
|
209
|
+
validator = SvgConform::Validator.new
|
|
210
|
+
initial_result = validator.validate_file(file,
|
|
211
|
+
profile: @options[:profile].to_sym)
|
|
212
|
+
|
|
213
|
+
file_result.valid_before = initial_result.valid?
|
|
214
|
+
file_result.errors_before = initial_result.errors.count
|
|
215
|
+
|
|
216
|
+
unless @options[:quiet]
|
|
217
|
+
if initial_result.valid?
|
|
218
|
+
puts " ✓ Already valid"
|
|
219
|
+
else
|
|
220
|
+
puts " ✗ Invalid: #{initial_result.errors.count} errors"
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Handle remediation if requested
|
|
225
|
+
if @options[:fix] && !initial_result.valid?
|
|
226
|
+
profile = SvgConform::Profiles.get(@options[:profile].to_sym)
|
|
227
|
+
runner = SvgConform::RemediationRunner.new(profile: profile)
|
|
228
|
+
remediation_result = runner.run_remediation_file(file)
|
|
229
|
+
|
|
230
|
+
if remediation_result.remediated_content
|
|
231
|
+
output_path = determine_output_path(file)
|
|
232
|
+
File.write(output_path, remediation_result.remediated_content)
|
|
233
|
+
|
|
234
|
+
file_result.valid_after = remediation_result.final_validation.valid?
|
|
235
|
+
file_result.errors_after = remediation_result.final_validation.errors.count
|
|
236
|
+
file_result.remediated_path = output_path
|
|
237
|
+
file_result.status = file_result.valid_after ? "remediated" : "failed"
|
|
238
|
+
|
|
239
|
+
unless @options[:quiet]
|
|
240
|
+
if file_result.valid_after
|
|
241
|
+
puts " ✓ Remediated successfully"
|
|
242
|
+
else
|
|
243
|
+
puts " ⚠ #{file_result.errors_after} errors remain"
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
file_result.valid_after = false
|
|
248
|
+
file_result.errors_after = initial_result.errors.count
|
|
249
|
+
file_result.status = "failed"
|
|
250
|
+
unless @options[:quiet]
|
|
251
|
+
puts " ✗ Remediation failed"
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
else
|
|
255
|
+
# No remediation requested or already valid
|
|
256
|
+
file_result.valid_after = file_result.valid_before
|
|
257
|
+
file_result.errors_after = file_result.errors_before
|
|
258
|
+
file_result.status = file_result.valid_before ? "valid" : "failed"
|
|
259
|
+
end
|
|
260
|
+
rescue StandardError => e
|
|
261
|
+
file_result.status = "error"
|
|
262
|
+
file_result.error_message = e.message
|
|
263
|
+
file_result.valid_after = false
|
|
264
|
+
file_result.errors_after = -1
|
|
265
|
+
unless @options[:quiet]
|
|
266
|
+
puts " ✗ Error: #{e.message}"
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
file_result
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def validate_output_options(files)
|
|
274
|
+
return if @options[:in_place]
|
|
275
|
+
|
|
276
|
+
# Multiple files require output-dir
|
|
277
|
+
if files.length > 1 && !@options[:output_dir]
|
|
278
|
+
puts Paint["Error: --output-dir required for multiple files with --fix",
|
|
279
|
+
:red]
|
|
280
|
+
exit 1
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# In-place requires force
|
|
284
|
+
if @options[:in_place] && !@options[:force]
|
|
285
|
+
puts Paint["Error: --in-place requires --force flag for safety", :red]
|
|
286
|
+
exit 1
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def prepare_output_directory
|
|
291
|
+
return if @options[:in_place]
|
|
292
|
+
|
|
293
|
+
output_dir = @options[:output_dir]
|
|
294
|
+
if output_dir
|
|
295
|
+
FileUtils.mkdir_p(output_dir)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def determine_output_path(file)
|
|
300
|
+
if @options[:in_place]
|
|
301
|
+
file
|
|
302
|
+
elsif @options[:output_dir]
|
|
303
|
+
File.join(@options[:output_dir], File.basename(file))
|
|
304
|
+
else
|
|
305
|
+
"#{file}.fixed.svg"
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def output_batch_results(batch_report)
|
|
310
|
+
puts
|
|
311
|
+
puts "=" * 80
|
|
312
|
+
puts Paint["BATCH VALIDATION SUMMARY", :bold]
|
|
313
|
+
puts "=" * 80
|
|
314
|
+
puts "Directory: #{batch_report.directory}"
|
|
315
|
+
puts "Profile: #{batch_report.profile}"
|
|
316
|
+
puts "Files processed: #{batch_report.total_files}"
|
|
317
|
+
puts "Valid before: #{batch_report.valid_before}"
|
|
318
|
+
if @options[:fix]
|
|
319
|
+
puts "Remediated: #{batch_report.remediated}"
|
|
320
|
+
end
|
|
321
|
+
puts "Valid after: #{batch_report.valid_after}"
|
|
322
|
+
puts "Failed: #{batch_report.failed}"
|
|
323
|
+
puts "Success rate: #{batch_report.success_rate}%"
|
|
324
|
+
puts "=" * 80
|
|
325
|
+
|
|
326
|
+
# Output detailed report if requested
|
|
327
|
+
if @options[:report_format] && @options[:report_output]
|
|
328
|
+
case @options[:report_format]
|
|
329
|
+
when "json"
|
|
330
|
+
File.write(@options[:report_output], batch_report.to_json)
|
|
331
|
+
puts Paint["Detailed JSON report written to #{@options[:report_output]}",
|
|
332
|
+
:green]
|
|
333
|
+
when "yaml"
|
|
334
|
+
File.write(@options[:report_output], batch_report.to_yaml)
|
|
335
|
+
puts Paint["Detailed YAML report written to #{@options[:report_output]}",
|
|
336
|
+
:green]
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def create_manifest_file(batch_report)
|
|
342
|
+
return if batch_report.manifest.empty?
|
|
343
|
+
|
|
344
|
+
manifest_path = @options[:manifest] || "manifest.json"
|
|
345
|
+
manifest_data = {
|
|
346
|
+
timestamp: batch_report.timestamp,
|
|
347
|
+
profile: batch_report.profile,
|
|
348
|
+
mappings: batch_report.manifest,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
File.write(manifest_path, JSON.pretty_generate(manifest_data))
|
|
352
|
+
puts Paint["Manifest written to #{manifest_path}", :green]
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def create_remediated_file(_result)
|
|
356
|
+
# Load the profile for remediation
|
|
357
|
+
profile = SvgConform::Profiles.get(@options[:profile].to_sym)
|
|
358
|
+
|
|
359
|
+
# Create remediation runner
|
|
360
|
+
runner = SvgConform::RemediationRunner.new(profile: profile)
|
|
361
|
+
|
|
362
|
+
# Run remediation
|
|
363
|
+
remediation_result = runner.run_remediation_file(@file)
|
|
364
|
+
|
|
365
|
+
if remediation_result.success? && remediation_result.remediated_content
|
|
366
|
+
output_file = @options[:fix_output] || "#{@file}.fixed.svg"
|
|
367
|
+
File.write(output_file, remediation_result.remediated_content)
|
|
368
|
+
puts Paint["Remediated file written to #{output_file}", :green]
|
|
369
|
+
|
|
370
|
+
# Show remediation summary
|
|
371
|
+
if remediation_result.issues_fixed.positive?
|
|
372
|
+
puts Paint["✓ Fixed #{remediation_result.issues_fixed} issue(s)",
|
|
373
|
+
:green]
|
|
374
|
+
else
|
|
375
|
+
puts Paint["No issues were fixed (file may already be valid)",
|
|
376
|
+
:yellow]
|
|
377
|
+
end
|
|
378
|
+
else
|
|
379
|
+
puts Paint["Warning: Could not create remediated file", :yellow]
|
|
380
|
+
if remediation_result.error?
|
|
381
|
+
puts Paint["Error: #{remediation_result.error.message}",
|
|
382
|
+
:red]
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
rescue StandardError => e
|
|
386
|
+
puts Paint["Error creating remediated file: #{e.message}", :red]
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "paint"
|
|
4
|
+
require "table_tennis"
|
|
5
|
+
|
|
6
|
+
module SvgConform
|
|
7
|
+
module Commands
|
|
8
|
+
# Profiles command for listing available validation profiles
|
|
9
|
+
class Profiles
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
# Paint doesn't need initialization like Pastel
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def execute
|
|
16
|
+
profiles = load_available_profiles
|
|
17
|
+
|
|
18
|
+
if @options[:verbose]
|
|
19
|
+
display_detailed_profiles(profiles)
|
|
20
|
+
else
|
|
21
|
+
display_profile_list(profiles)
|
|
22
|
+
end
|
|
23
|
+
rescue StandardError => e
|
|
24
|
+
puts Paint["Error: #{e.message}", :red]
|
|
25
|
+
exit 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def load_available_profiles
|
|
31
|
+
# Get all profile files
|
|
32
|
+
profile_dir = File.join(File.dirname(__FILE__),
|
|
33
|
+
"../../../config/profiles")
|
|
34
|
+
profile_files = Dir.glob(File.join(profile_dir, "*.yml"))
|
|
35
|
+
|
|
36
|
+
profiles = {}
|
|
37
|
+
|
|
38
|
+
profile_files.each do |file|
|
|
39
|
+
name = File.basename(file, ".yml")
|
|
40
|
+
begin
|
|
41
|
+
profile = SvgConform::Profile.load_from_file(file)
|
|
42
|
+
profiles[name] = profile
|
|
43
|
+
rescue StandardError => e
|
|
44
|
+
puts Paint["Warning: Could not load profile '#{name}': #{e.message}",
|
|
45
|
+
:yellow]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
profiles
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def display_profile_list(profiles)
|
|
53
|
+
puts Paint["Available SVG Profiles", :bold]
|
|
54
|
+
puts "=" * 40
|
|
55
|
+
puts
|
|
56
|
+
|
|
57
|
+
if profiles.empty?
|
|
58
|
+
puts Paint["No profiles found", :yellow]
|
|
59
|
+
return
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Create table data - table_tennis handles its own formatting
|
|
63
|
+
table_data = profiles.map do |name, profile|
|
|
64
|
+
description = profile.description || "No description available"
|
|
65
|
+
{ profile: name, description: description }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Use table_tennis with headers option
|
|
69
|
+
table = TableTennis.new(table_data,
|
|
70
|
+
headers: { profile: "Profile",
|
|
71
|
+
description: "Description" })
|
|
72
|
+
puts table
|
|
73
|
+
puts
|
|
74
|
+
puts Paint["Use --verbose for detailed information about each profile",
|
|
75
|
+
:black]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def display_detailed_profiles(profiles)
|
|
79
|
+
puts Paint["Detailed SVG Validation Profiles", :bold]
|
|
80
|
+
puts "=" * 50
|
|
81
|
+
puts
|
|
82
|
+
|
|
83
|
+
profiles.each do |name, profile|
|
|
84
|
+
puts Paint["Profile: #{name}", :bold]
|
|
85
|
+
puts Paint["-" * 30, :black]
|
|
86
|
+
|
|
87
|
+
puts "Description: #{profile.description || 'No description available'}"
|
|
88
|
+
puts "Requirements: #{profile.requirement_count}"
|
|
89
|
+
puts "Remediations: #{profile.remediation_count}"
|
|
90
|
+
|
|
91
|
+
if profile.requirements&.any?
|
|
92
|
+
puts "Requirement Details:"
|
|
93
|
+
profile.requirements.each do |requirement|
|
|
94
|
+
puts " • #{requirement.class.name.split('::').last}"
|
|
95
|
+
if requirement.respond_to?(:description) && requirement.description
|
|
96
|
+
puts Paint[" #{requirement.description}",
|
|
97
|
+
:black]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if profile.remediations&.any?
|
|
103
|
+
puts "Remediation Details:"
|
|
104
|
+
profile.remediations.each do |remediation|
|
|
105
|
+
puts " • #{remediation.class.name.split('::').last}"
|
|
106
|
+
if remediation.respond_to?(:description) && remediation.description
|
|
107
|
+
puts Paint[" #{remediation.description}",
|
|
108
|
+
:black]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
puts
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require "paint"
|
|
5
|
+
require_relative "svgcheck_compare"
|
|
6
|
+
require_relative "svgcheck_compatibility"
|
|
7
|
+
require_relative "svgcheck_generate"
|
|
8
|
+
|
|
9
|
+
module SvgConform
|
|
10
|
+
module Commands
|
|
11
|
+
# Svgcheck subcommand for svgcheck-related operations
|
|
12
|
+
class Svgcheck < Thor
|
|
13
|
+
desc "compare FILE", "Compare SvgConform validation with svgcheck report"
|
|
14
|
+
long_desc <<~DESC
|
|
15
|
+
Compare SvgConform validation results with existing svgcheck reports.
|
|
16
|
+
|
|
17
|
+
This command looks for a corresponding .svgcheck.yaml file and compares
|
|
18
|
+
the validation results, showing differences in a detailed table format.
|
|
19
|
+
DESC
|
|
20
|
+
option :profile, aliases: "-p", default: "svg_1_2_rfc",
|
|
21
|
+
desc: "Profile to validate against"
|
|
22
|
+
option :svgcheck_report,
|
|
23
|
+
desc: "Path to svgcheck report (default: auto-detect)"
|
|
24
|
+
def compare(file)
|
|
25
|
+
SvgConform::Commands::SvgcheckCompare.new(file, options).execute
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "compatibility", "Run comprehensive svgcheck compatibility analysis"
|
|
29
|
+
long_desc <<~DESC
|
|
30
|
+
Run a comprehensive analysis comparing SvgConform validation results
|
|
31
|
+
with svgcheck outputs in check or repair mode.
|
|
32
|
+
|
|
33
|
+
MODES:
|
|
34
|
+
- check: Compare validation results using svgcheck check mode outputs
|
|
35
|
+
- repair: Compare remediation results using svgcheck repair mode outputs
|
|
36
|
+
|
|
37
|
+
Use --file to analyze a specific file instead of all test files.
|
|
38
|
+
Use --svgcheck-dir to specify where to find svgcheck outputs.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
svg_conform svgcheck compatibility --mode check
|
|
42
|
+
svg_conform svgcheck compatibility --file viewBox-none.svg
|
|
43
|
+
DESC
|
|
44
|
+
option :profile, aliases: "-p", default: "svg_1_2_rfc",
|
|
45
|
+
desc: "Profile to validate against"
|
|
46
|
+
option :output, aliases: "-o", desc: "Output file for detailed report"
|
|
47
|
+
option :file, aliases: "-f",
|
|
48
|
+
desc: "Analyze specific file instead of all test files"
|
|
49
|
+
option :mode, aliases: "-m", default: "check", enum: %w[check repair],
|
|
50
|
+
desc: "Comparison mode: check or repair"
|
|
51
|
+
option :semantic, type: :boolean, default: false,
|
|
52
|
+
desc: "Use semantic comparison"
|
|
53
|
+
option :svgcheck_dir, aliases: "-d", default: "spec/fixtures/svgcheck",
|
|
54
|
+
desc: "Directory containing svgcheck outputs"
|
|
55
|
+
def compatibility
|
|
56
|
+
SvgConform::Commands::SvgcheckCompatibility.new(options).execute
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
desc "generate SVGCHECK_REPO_PATH", "Generate svgcheck outputs"
|
|
60
|
+
long_desc <<~DESC
|
|
61
|
+
Generate svgcheck outputs for test files by running svgcheck on them.
|
|
62
|
+
|
|
63
|
+
SVGCHECK_REPO_PATH: Path to the svgcheck repository (e.g., svgcheck-reference)
|
|
64
|
+
|
|
65
|
+
By default, processes all test files in SVGCHECK_REPO_PATH/svgcheck/Tests/
|
|
66
|
+
and generates BOTH check and repair outputs in separate subdirectories.
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
svg_conform svgcheck generate svgcheck-reference
|
|
70
|
+
svg_conform svgcheck generate svgcheck-reference --mode check
|
|
71
|
+
svg_conform svgcheck generate svgcheck-reference --single-file example.svg
|
|
72
|
+
DESC
|
|
73
|
+
option :mode, aliases: "-m", default: "both", enum: %w[check repair both],
|
|
74
|
+
desc: "Generation mode: check, repair, or both"
|
|
75
|
+
option :svgcheck_exec,
|
|
76
|
+
desc: "Path to svgcheck executable"
|
|
77
|
+
option :fixtures_path,
|
|
78
|
+
desc: "Output directory (default: spec/fixtures/svgcheck)"
|
|
79
|
+
option :single_file, aliases: "-f", desc: "Process single file only"
|
|
80
|
+
option :force, type: :boolean, default: false,
|
|
81
|
+
desc: "Overwrite existing outputs"
|
|
82
|
+
option :verbose, aliases: "-v", type: :boolean, default: false,
|
|
83
|
+
desc: "Verbose output"
|
|
84
|
+
def generate(svgcheck_repo_path)
|
|
85
|
+
SvgConform::Commands::SvgcheckGenerate.new(svgcheck_repo_path,
|
|
86
|
+
options).execute
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "paint"
|
|
4
|
+
|
|
5
|
+
module SvgConform
|
|
6
|
+
module Commands
|
|
7
|
+
# Compare command for comparing SvgConform validation with svgcheck reports
|
|
8
|
+
class SvgcheckCompare
|
|
9
|
+
def initialize(file, options)
|
|
10
|
+
@file = file
|
|
11
|
+
@options = options
|
|
12
|
+
# Paint doesn't need initialization like Pastel
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def execute
|
|
16
|
+
# Determine the correct file to validate
|
|
17
|
+
validation_file = determine_validation_file(@file)
|
|
18
|
+
|
|
19
|
+
unless File.exist?(validation_file)
|
|
20
|
+
puts Paint["Error: File '#{validation_file}' not found", :red]
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
# Find or use specified svgcheck report
|
|
26
|
+
svgcheck_report_path = find_svgcheck_report
|
|
27
|
+
unless svgcheck_report_path && File.exist?(svgcheck_report_path)
|
|
28
|
+
puts Paint["Error: svgcheck report not found", :red]
|
|
29
|
+
if svgcheck_report_path
|
|
30
|
+
puts Paint["Expected: #{svgcheck_report_path}",
|
|
31
|
+
:dim]
|
|
32
|
+
end
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Load svgcheck report using the new external checker parser
|
|
37
|
+
parser = SvgConform::ExternalCheckers::Svgcheck::Parser.new
|
|
38
|
+
error_content = File.read(svgcheck_report_path)
|
|
39
|
+
svgcheck_report = parser.parse(error_content, nil,
|
|
40
|
+
filename: File.basename(@file))
|
|
41
|
+
|
|
42
|
+
# Generate SvgConform report using the correct validation file
|
|
43
|
+
validator = SvgConform::Validator.new
|
|
44
|
+
result = validator.validate_file(validation_file,
|
|
45
|
+
profile: @options[:profile].to_sym)
|
|
46
|
+
svg_conform_report = SvgConform::ConformanceReport.from_svg_conform_result(
|
|
47
|
+
File.basename(@file),
|
|
48
|
+
result,
|
|
49
|
+
profile: @options[:profile].to_sym,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Compare the reports
|
|
53
|
+
comparator = SvgConform::ReportComparator.new
|
|
54
|
+
comparator.compare_reports(svg_conform_report, svgcheck_report,
|
|
55
|
+
File.basename(@file))
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
puts Paint["Error: #{e.message}", :red]
|
|
58
|
+
exit 1
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def determine_validation_file(file)
|
|
65
|
+
# If the file is in a Results directory, try to find the original in Tests directory
|
|
66
|
+
if file.include?("/Results/")
|
|
67
|
+
test_file = file.gsub("/Results/", "/Tests/")
|
|
68
|
+
return test_file if File.exist?(test_file)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Otherwise use the original file
|
|
72
|
+
file
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def find_svgcheck_report
|
|
76
|
+
return @options[:svgcheck_report] if @options[:svgcheck_report]
|
|
77
|
+
|
|
78
|
+
# Auto-detect based on file name
|
|
79
|
+
base_name = File.basename(@file)
|
|
80
|
+
|
|
81
|
+
# Try common locations
|
|
82
|
+
possible_paths = [
|
|
83
|
+
"spec/fixtures/svgcheck_reports/#{base_name}.svgcheck.yaml",
|
|
84
|
+
"#{File.dirname(@file)}/#{base_name}.svgcheck.yaml",
|
|
85
|
+
"#{@file}.svgcheck.yaml",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
possible_paths.find { |path| File.exist?(path) }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|