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.
Files changed (335) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rake.yml +15 -0
  3. data/.github/workflows/release.yml +23 -0
  4. data/.github/workflows/svgcheck-compatibility.yml +135 -0
  5. data/.gitignore +12 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +13 -0
  8. data/.rubocop_todo.yml +183 -0
  9. data/CODE_OF_CONDUCT.md +132 -0
  10. data/Gemfile +14 -0
  11. data/README.adoc +1384 -0
  12. data/Rakefile +15 -0
  13. data/config/profiles/base.yml +34 -0
  14. data/config/profiles/lucid_fix.yml +37 -0
  15. data/config/profiles/metanorma.yml +212 -0
  16. data/config/profiles/no_external_css.yml +20 -0
  17. data/config/profiles/svg_1_2_rfc.yml +284 -0
  18. data/config/profiles/svg_1_2_rfc_with_rdf.yml +145 -0
  19. data/config/svgcheck_mapping.yml +180 -0
  20. data/docs/profiles.adoc +547 -0
  21. data/docs/rdf_metadata_support.adoc +212 -0
  22. data/docs/remediation.adoc +732 -0
  23. data/docs/requirements.adoc +709 -0
  24. data/examples/demo.rb +116 -0
  25. data/examples/requirements_demo.rb +240 -0
  26. data/exe/svg_conform +7 -0
  27. data/lib/svg_conform/batch_report.rb +70 -0
  28. data/lib/svg_conform/cli.rb +107 -0
  29. data/lib/svg_conform/commands/check.rb +390 -0
  30. data/lib/svg_conform/commands/profiles.rb +118 -0
  31. data/lib/svg_conform/commands/svgcheck.rb +90 -0
  32. data/lib/svg_conform/commands/svgcheck_compare.rb +92 -0
  33. data/lib/svg_conform/commands/svgcheck_compatibility.rb +43 -0
  34. data/lib/svg_conform/commands/svgcheck_generate.rb +262 -0
  35. data/lib/svg_conform/compatibility/analysis_context.rb +68 -0
  36. data/lib/svg_conform/compatibility/comparison_result.rb +147 -0
  37. data/lib/svg_conform/compatibility/compatibility_analyzer.rb +85 -0
  38. data/lib/svg_conform/compatibility/file_processor.rb +109 -0
  39. data/lib/svg_conform/compatibility/pattern_discovery.rb +319 -0
  40. data/lib/svg_conform/compatibility/report_formatter.rb +359 -0
  41. data/lib/svg_conform/compatibility/svg_analysis_engine.rb +316 -0
  42. data/lib/svg_conform/compatibility/validity_analysis.rb +252 -0
  43. data/lib/svg_conform/compatibility/xml_analysis_engine.rb +198 -0
  44. data/lib/svg_conform/compatibility_analyzer.rb +285 -0
  45. data/lib/svg_conform/conformance_report.rb +267 -0
  46. data/lib/svg_conform/constants.rb +199 -0
  47. data/lib/svg_conform/css_color.rb +262 -0
  48. data/lib/svg_conform/document.rb +203 -0
  49. data/lib/svg_conform/external_checkers/svgcheck/compatibility_engine.rb +166 -0
  50. data/lib/svg_conform/external_checkers/svgcheck/output_generator.rb +101 -0
  51. data/lib/svg_conform/external_checkers/svgcheck/parser.rb +200 -0
  52. data/lib/svg_conform/external_checkers/svgcheck/report_comparator.rb +175 -0
  53. data/lib/svg_conform/external_checkers/svgcheck/report_generator.rb +82 -0
  54. data/lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb +249 -0
  55. data/lib/svg_conform/external_checkers/svgcheck.rb +56 -0
  56. data/lib/svg_conform/external_checkers.rb +34 -0
  57. data/lib/svg_conform/fixer.rb +56 -0
  58. data/lib/svg_conform/profile.rb +164 -0
  59. data/lib/svg_conform/profiles.rb +60 -0
  60. data/lib/svg_conform/remediation_engine.rb +92 -0
  61. data/lib/svg_conform/remediation_result.rb +36 -0
  62. data/lib/svg_conform/remediation_runner.rb +225 -0
  63. data/lib/svg_conform/remediations/base_remediation.rb +165 -0
  64. data/lib/svg_conform/remediations/color_remediation.rb +226 -0
  65. data/lib/svg_conform/remediations/font_embedding_remediation.rb +145 -0
  66. data/lib/svg_conform/remediations/font_remediation.rb +122 -0
  67. data/lib/svg_conform/remediations/image_embedding_remediation.rb +154 -0
  68. data/lib/svg_conform/remediations/invalid_id_references_remediation.rb +129 -0
  69. data/lib/svg_conform/remediations/namespace_attribute_remediation.rb +244 -0
  70. data/lib/svg_conform/remediations/namespace_remediation.rb +151 -0
  71. data/lib/svg_conform/remediations/no_external_css_remediation.rb +192 -0
  72. data/lib/svg_conform/remediations/style_promotion_remediation.rb +93 -0
  73. data/lib/svg_conform/remediations/viewbox_remediation.rb +127 -0
  74. data/lib/svg_conform/remediations.rb +40 -0
  75. data/lib/svg_conform/report_comparator.rb +772 -0
  76. data/lib/svg_conform/requirements/allowed_elements_requirement.rb +367 -0
  77. data/lib/svg_conform/requirements/base_requirement.rb +98 -0
  78. data/lib/svg_conform/requirements/color_restrictions_requirement.rb +126 -0
  79. data/lib/svg_conform/requirements/element_requirement_config.rb +75 -0
  80. data/lib/svg_conform/requirements/font_family_requirement.rb +133 -0
  81. data/lib/svg_conform/requirements/forbidden_content_requirement.rb +60 -0
  82. data/lib/svg_conform/requirements/id_reference_requirement.rb +133 -0
  83. data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +132 -0
  84. data/lib/svg_conform/requirements/link_validation_requirement.rb +55 -0
  85. data/lib/svg_conform/requirements/namespace_attributes_requirement.rb +211 -0
  86. data/lib/svg_conform/requirements/namespace_requirement.rb +294 -0
  87. data/lib/svg_conform/requirements/no_external_css_requirement.rb +132 -0
  88. data/lib/svg_conform/requirements/no_external_fonts_requirement.rb +121 -0
  89. data/lib/svg_conform/requirements/no_external_images_requirement.rb +91 -0
  90. data/lib/svg_conform/requirements/style_promotion_requirement.rb +72 -0
  91. data/lib/svg_conform/requirements/style_requirement.rb +226 -0
  92. data/lib/svg_conform/requirements/viewbox_required_requirement.rb +96 -0
  93. data/lib/svg_conform/requirements.rb +49 -0
  94. data/lib/svg_conform/semantic_comparator.rb +829 -0
  95. data/lib/svg_conform/validation_context.rb +408 -0
  96. data/lib/svg_conform/validation_result.rb +146 -0
  97. data/lib/svg_conform/validator.rb +91 -0
  98. data/lib/svg_conform/version.rb +5 -0
  99. data/lib/svg_conform.rb +68 -0
  100. data/lib/tasks/fixtures.rake +321 -0
  101. data/lib/tasks/svgcheck.rake +111 -0
  102. data/reference-docs/SVG-1.2-RFC.rnc.txt +1676 -0
  103. data/reference-docs/Scalable Vector Graphics (SVG) 1.1 (Second Edition).html +40764 -0
  104. data/reference-docs/Scalable Vector Graphics (SVG) Tiny 1.2 Specification.html +44591 -0
  105. data/reference-docs/rfc7996.txt +2971 -0
  106. data/sig/svg_conform.rbs +4 -0
  107. data/spec/fixtures/allowed_elements/inputs/basic_violations.svg +21 -0
  108. data/spec/fixtures/allowed_elements/repair/basic_violations.svg +18 -0
  109. data/spec/fixtures/color_restrictions/inputs/basic_violations.svg +23 -0
  110. data/spec/fixtures/color_restrictions/repair/basic_violations.svg +23 -0
  111. data/spec/fixtures/comprehensive/inputs/multiple_violations.svg +21 -0
  112. data/spec/fixtures/comprehensive/repair/multiple_violations.svg +16 -0
  113. data/spec/fixtures/font_family/inputs/basic_violations.svg +17 -0
  114. data/spec/fixtures/font_family/repair/basic_violations.svg +17 -0
  115. data/spec/fixtures/forbidden_content/inputs/basic_violations.svg +27 -0
  116. data/spec/fixtures/forbidden_content/repair/basic_violations.svg +19 -0
  117. data/spec/fixtures/id_reference/inputs/basic_violations.svg +17 -0
  118. data/spec/fixtures/id_reference/repair/basic_violations.svg +15 -0
  119. data/spec/fixtures/link_validation/inputs/basic_violations.svg +20 -0
  120. data/spec/fixtures/link_validation/repair/basic_violations.svg +16 -0
  121. data/spec/fixtures/lucid/inputs/simple.svg +67 -0
  122. data/spec/fixtures/lucid/repair/simple.svg +63 -0
  123. data/spec/fixtures/namespace/inputs/basic_violations.svg +20 -0
  124. data/spec/fixtures/namespace/repair/basic_violations.svg +17 -0
  125. data/spec/fixtures/namespace_attributes/inputs/basic_violations.svg +16 -0
  126. data/spec/fixtures/namespace_attributes/repair/basic_violations.svg +15 -0
  127. data/spec/fixtures/no_external_css/inputs/basic_violations.svg +20 -0
  128. data/spec/fixtures/no_external_css/repair/basic_violations.svg +18 -0
  129. data/spec/fixtures/style/inputs/basic_violations.svg +22 -0
  130. data/spec/fixtures/style/repair/basic_violations.svg +22 -0
  131. data/spec/fixtures/style_promotion/inputs/basic_test.svg +15 -0
  132. data/spec/fixtures/style_promotion/repair/basic_test.svg +15 -0
  133. data/spec/fixtures/svg_1_2_rfc/inputs/allowed_elements_violations.svg +18 -0
  134. data/spec/fixtures/svg_1_2_rfc/inputs/color_restrictions_violations.svg +23 -0
  135. data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.code +1 -0
  136. data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.err +0 -0
  137. data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.out +23 -0
  138. data/spec/fixtures/svgcheck/check/IETF-test.svg.code +1 -0
  139. data/spec/fixtures/svgcheck/check/IETF-test.svg.err +0 -0
  140. data/spec/fixtures/svgcheck/check/IETF-test.svg.out +20 -0
  141. data/spec/fixtures/svgcheck/check/circle.svg.code +1 -0
  142. data/spec/fixtures/svgcheck/check/circle.svg.err +0 -0
  143. data/spec/fixtures/svgcheck/check/circle.svg.out +2 -0
  144. data/spec/fixtures/svgcheck/check/colors.svg.code +1 -0
  145. data/spec/fixtures/svgcheck/check/colors.svg.err +0 -0
  146. data/spec/fixtures/svgcheck/check/colors.svg.out +13 -0
  147. data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.code +1 -0
  148. data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.err +0 -0
  149. data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.out +76 -0
  150. data/spec/fixtures/svgcheck/check/example-dot.svg.code +1 -0
  151. data/spec/fixtures/svgcheck/check/example-dot.svg.err +0 -0
  152. data/spec/fixtures/svgcheck/check/example-dot.svg.out +11 -0
  153. data/spec/fixtures/svgcheck/check/full-tiny.svg.code +1 -0
  154. data/spec/fixtures/svgcheck/check/full-tiny.svg.err +0 -0
  155. data/spec/fixtures/svgcheck/check/full-tiny.svg.out +5835 -0
  156. data/spec/fixtures/svgcheck/check/good.svg.code +1 -0
  157. data/spec/fixtures/svgcheck/check/good.svg.err +0 -0
  158. data/spec/fixtures/svgcheck/check/good.svg.out +1 -0
  159. data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.code +1 -0
  160. data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.err +0 -0
  161. data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.out +5 -0
  162. data/spec/fixtures/svgcheck/check/malformed.svg.code +1 -0
  163. data/spec/fixtures/svgcheck/check/malformed.svg.err +0 -0
  164. data/spec/fixtures/svgcheck/check/malformed.svg.out +8 -0
  165. data/spec/fixtures/svgcheck/check/rfc-svg.svg.code +1 -0
  166. data/spec/fixtures/svgcheck/check/rfc-svg.svg.err +0 -0
  167. data/spec/fixtures/svgcheck/check/rfc-svg.svg.out +1 -0
  168. data/spec/fixtures/svgcheck/check/rfc.xml.code +1 -0
  169. data/spec/fixtures/svgcheck/check/rfc.xml.err +0 -0
  170. data/spec/fixtures/svgcheck/check/rfc.xml.out +2 -0
  171. data/spec/fixtures/svgcheck/check/rgb.svg.code +1 -0
  172. data/spec/fixtures/svgcheck/check/rgb.svg.err +0 -0
  173. data/spec/fixtures/svgcheck/check/rgb.svg.out +9 -0
  174. data/spec/fixtures/svgcheck/check/svg-wordle.svg.code +1 -0
  175. data/spec/fixtures/svgcheck/check/svg-wordle.svg.err +0 -0
  176. data/spec/fixtures/svgcheck/check/svg-wordle.svg.out +508 -0
  177. data/spec/fixtures/svgcheck/check/threshold.svg.code +1 -0
  178. data/spec/fixtures/svgcheck/check/threshold.svg.err +0 -0
  179. data/spec/fixtures/svgcheck/check/threshold.svg.out +20 -0
  180. data/spec/fixtures/svgcheck/check/utf8.svg.code +1 -0
  181. data/spec/fixtures/svgcheck/check/utf8.svg.err +0 -0
  182. data/spec/fixtures/svgcheck/check/utf8.svg.out +162 -0
  183. data/spec/fixtures/svgcheck/check/viewBox-both.svg.code +1 -0
  184. data/spec/fixtures/svgcheck/check/viewBox-both.svg.err +0 -0
  185. data/spec/fixtures/svgcheck/check/viewBox-both.svg.out +4 -0
  186. data/spec/fixtures/svgcheck/check/viewBox-height.svg.code +1 -0
  187. data/spec/fixtures/svgcheck/check/viewBox-height.svg.err +0 -0
  188. data/spec/fixtures/svgcheck/check/viewBox-height.svg.out +3 -0
  189. data/spec/fixtures/svgcheck/check/viewBox-none.svg.code +1 -0
  190. data/spec/fixtures/svgcheck/check/viewBox-none.svg.err +0 -0
  191. data/spec/fixtures/svgcheck/check/viewBox-none.svg.out +3 -0
  192. data/spec/fixtures/svgcheck/check/viewBox-width.svg.code +1 -0
  193. data/spec/fixtures/svgcheck/check/viewBox-width.svg.err +0 -0
  194. data/spec/fixtures/svgcheck/check/viewBox-width.svg.out +3 -0
  195. data/spec/fixtures/svgcheck/inputs/DrawBerry-sample-2.svg +28 -0
  196. data/spec/fixtures/svgcheck/inputs/IETF-test.svg +28 -0
  197. data/spec/fixtures/svgcheck/inputs/circle.svg +3 -0
  198. data/spec/fixtures/svgcheck/inputs/colors.svg +18 -0
  199. data/spec/fixtures/svgcheck/inputs/dia-sample-svg.svg +47 -0
  200. data/spec/fixtures/svgcheck/inputs/example-dot.svg +75 -0
  201. data/spec/fixtures/svgcheck/inputs/full-tiny.svg +16194 -0
  202. data/spec/fixtures/svgcheck/inputs/good.svg +19 -0
  203. data/spec/fixtures/svgcheck/inputs/httpbis-proxy20-fig6.svg +2 -0
  204. data/spec/fixtures/svgcheck/inputs/malformed.svg +11 -0
  205. data/spec/fixtures/svgcheck/inputs/rfc-svg.svg +1028 -0
  206. data/spec/fixtures/svgcheck/inputs/rfc.xml +37 -0
  207. data/spec/fixtures/svgcheck/inputs/rgb.svg +9 -0
  208. data/spec/fixtures/svgcheck/inputs/svg-wordle.svg +330 -0
  209. data/spec/fixtures/svgcheck/inputs/threshold.svg +26 -0
  210. data/spec/fixtures/svgcheck/inputs/utf8.svg +448 -0
  211. data/spec/fixtures/svgcheck/inputs/viewBox-both.svg +3 -0
  212. data/spec/fixtures/svgcheck/inputs/viewBox-height.svg +3 -0
  213. data/spec/fixtures/svgcheck/inputs/viewBox-none.svg +3 -0
  214. data/spec/fixtures/svgcheck/inputs/viewBox-width.svg +3 -0
  215. data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.code +1 -0
  216. data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.err +0 -0
  217. data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.file +0 -0
  218. data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.out +23 -0
  219. data/spec/fixtures/svgcheck/repair/IETF-test.svg.code +1 -0
  220. data/spec/fixtures/svgcheck/repair/IETF-test.svg.err +0 -0
  221. data/spec/fixtures/svgcheck/repair/IETF-test.svg.file +29 -0
  222. data/spec/fixtures/svgcheck/repair/IETF-test.svg.out +20 -0
  223. data/spec/fixtures/svgcheck/repair/circle.svg.code +1 -0
  224. data/spec/fixtures/svgcheck/repair/circle.svg.err +0 -0
  225. data/spec/fixtures/svgcheck/repair/circle.svg.file +4 -0
  226. data/spec/fixtures/svgcheck/repair/circle.svg.out +2 -0
  227. data/spec/fixtures/svgcheck/repair/colors.svg.code +1 -0
  228. data/spec/fixtures/svgcheck/repair/colors.svg.err +0 -0
  229. data/spec/fixtures/svgcheck/repair/colors.svg.file +19 -0
  230. data/spec/fixtures/svgcheck/repair/colors.svg.out +13 -0
  231. data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.code +1 -0
  232. data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.err +0 -0
  233. data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.file +47 -0
  234. data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.out +76 -0
  235. data/spec/fixtures/svgcheck/repair/example-dot.svg.code +1 -0
  236. data/spec/fixtures/svgcheck/repair/example-dot.svg.err +0 -0
  237. data/spec/fixtures/svgcheck/repair/example-dot.svg.file +69 -0
  238. data/spec/fixtures/svgcheck/repair/example-dot.svg.out +11 -0
  239. data/spec/fixtures/svgcheck/repair/good.svg.code +1 -0
  240. data/spec/fixtures/svgcheck/repair/good.svg.err +0 -0
  241. data/spec/fixtures/svgcheck/repair/good.svg.file +0 -0
  242. data/spec/fixtures/svgcheck/repair/good.svg.out +1 -0
  243. data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.code +1 -0
  244. data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.err +0 -0
  245. data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.file +73 -0
  246. data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.out +5 -0
  247. data/spec/fixtures/svgcheck/repair/malformed.svg.code +1 -0
  248. data/spec/fixtures/svgcheck/repair/malformed.svg.err +0 -0
  249. data/spec/fixtures/svgcheck/repair/malformed.svg.file +9 -0
  250. data/spec/fixtures/svgcheck/repair/malformed.svg.out +8 -0
  251. data/spec/fixtures/svgcheck/repair/rfc-svg.svg.code +1 -0
  252. data/spec/fixtures/svgcheck/repair/rfc-svg.svg.err +0 -0
  253. data/spec/fixtures/svgcheck/repair/rfc-svg.svg.file +0 -0
  254. data/spec/fixtures/svgcheck/repair/rfc-svg.svg.out +1 -0
  255. data/spec/fixtures/svgcheck/repair/rfc.xml.code +1 -0
  256. data/spec/fixtures/svgcheck/repair/rfc.xml.err +0 -0
  257. data/spec/fixtures/svgcheck/repair/rfc.xml.file +37 -0
  258. data/spec/fixtures/svgcheck/repair/rfc.xml.out +2 -0
  259. data/spec/fixtures/svgcheck/repair/rgb.svg.code +1 -0
  260. data/spec/fixtures/svgcheck/repair/rgb.svg.err +0 -0
  261. data/spec/fixtures/svgcheck/repair/rgb.svg.file +10 -0
  262. data/spec/fixtures/svgcheck/repair/rgb.svg.out +9 -0
  263. data/spec/fixtures/svgcheck/repair/svg-wordle.svg.code +1 -0
  264. data/spec/fixtures/svgcheck/repair/svg-wordle.svg.err +0 -0
  265. data/spec/fixtures/svgcheck/repair/svg-wordle.svg.file +112 -0
  266. data/spec/fixtures/svgcheck/repair/svg-wordle.svg.out +508 -0
  267. data/spec/fixtures/svgcheck/repair/threshold.svg.code +1 -0
  268. data/spec/fixtures/svgcheck/repair/threshold.svg.err +0 -0
  269. data/spec/fixtures/svgcheck/repair/threshold.svg.file +27 -0
  270. data/spec/fixtures/svgcheck/repair/threshold.svg.out +20 -0
  271. data/spec/fixtures/svgcheck/repair/utf8.svg.code +1 -0
  272. data/spec/fixtures/svgcheck/repair/utf8.svg.err +0 -0
  273. data/spec/fixtures/svgcheck/repair/utf8.svg.file +381 -0
  274. data/spec/fixtures/svgcheck/repair/utf8.svg.out +162 -0
  275. data/spec/fixtures/svgcheck/repair/viewBox-both.svg.code +1 -0
  276. data/spec/fixtures/svgcheck/repair/viewBox-both.svg.err +0 -0
  277. data/spec/fixtures/svgcheck/repair/viewBox-both.svg.file +4 -0
  278. data/spec/fixtures/svgcheck/repair/viewBox-both.svg.out +4 -0
  279. data/spec/fixtures/svgcheck/repair/viewBox-height.svg.code +1 -0
  280. data/spec/fixtures/svgcheck/repair/viewBox-height.svg.err +0 -0
  281. data/spec/fixtures/svgcheck/repair/viewBox-height.svg.file +4 -0
  282. data/spec/fixtures/svgcheck/repair/viewBox-height.svg.out +3 -0
  283. data/spec/fixtures/svgcheck/repair/viewBox-none.svg.code +1 -0
  284. data/spec/fixtures/svgcheck/repair/viewBox-none.svg.err +0 -0
  285. data/spec/fixtures/svgcheck/repair/viewBox-none.svg.file +4 -0
  286. data/spec/fixtures/svgcheck/repair/viewBox-none.svg.out +3 -0
  287. data/spec/fixtures/svgcheck/repair/viewBox-width.svg.code +1 -0
  288. data/spec/fixtures/svgcheck/repair/viewBox-width.svg.err +0 -0
  289. data/spec/fixtures/svgcheck/repair/viewBox-width.svg.file +4 -0
  290. data/spec/fixtures/svgcheck/repair/viewBox-width.svg.out +3 -0
  291. data/spec/fixtures/viewbox_required/inputs/missing_viewbox.svg +10 -0
  292. data/spec/fixtures/viewbox_required/repair/missing_viewbox.svg +10 -0
  293. data/spec/spec_helper.rb +16 -0
  294. data/spec/svg_conform/batch_report_spec.rb +99 -0
  295. data/spec/svg_conform/commands/check_command_spec.rb +90 -0
  296. data/spec/svg_conform/commands/profiles_command_spec.rb +20 -0
  297. data/spec/svg_conform/commands/svgcheck_compare_command_spec.rb +13 -0
  298. data/spec/svg_conform/commands/svgcheck_compatibility_command_spec.rb +13 -0
  299. data/spec/svg_conform/commands/svgcheck_generate_command_spec.rb +14 -0
  300. data/spec/svg_conform/profiles/base_profile_spec.rb +42 -0
  301. data/spec/svg_conform/profiles/lucid_fix_profile_spec.rb +46 -0
  302. data/spec/svg_conform/profiles/lucid_profile_spec.rb +84 -0
  303. data/spec/svg_conform/profiles/metanorma_profile_spec.rb +62 -0
  304. data/spec/svg_conform/profiles/no_external_css_profile_spec.rb +66 -0
  305. data/spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb +200 -0
  306. data/spec/svg_conform/profiles/svg_1_2_rfc_with_rdf_profile_spec.rb +81 -0
  307. data/spec/svg_conform/remediations/color_remediation_spec.rb +95 -0
  308. data/spec/svg_conform/remediations/font_embedding_remediation_spec.rb +20 -0
  309. data/spec/svg_conform/remediations/font_remediation_spec.rb +95 -0
  310. data/spec/svg_conform/remediations/image_embedding_remediation_spec.rb +20 -0
  311. data/spec/svg_conform/remediations/invalid_id_references_remediation_spec.rb +97 -0
  312. data/spec/svg_conform/remediations/namespace_attribute_remediation_spec.rb +97 -0
  313. data/spec/svg_conform/remediations/namespace_remediation_spec.rb +95 -0
  314. data/spec/svg_conform/remediations/no_external_css_remediation_spec.rb +97 -0
  315. data/spec/svg_conform/remediations/style_promotion_remediation_spec.rb +97 -0
  316. data/spec/svg_conform/remediations/viewbox_remediation_spec.rb +95 -0
  317. data/spec/svg_conform/requirements/allowed_elements_requirement_spec.rb +118 -0
  318. data/spec/svg_conform/requirements/color_restrictions_requirement_spec.rb +168 -0
  319. data/spec/svg_conform/requirements/font_family_requirement_spec.rb +188 -0
  320. data/spec/svg_conform/requirements/forbidden_content_requirement_spec.rb +195 -0
  321. data/spec/svg_conform/requirements/id_reference_requirement_spec.rb +78 -0
  322. data/spec/svg_conform/requirements/invalid_id_references_requirement_spec.rb +78 -0
  323. data/spec/svg_conform/requirements/link_validation_requirement_spec.rb +78 -0
  324. data/spec/svg_conform/requirements/namespace_attributes_requirement_spec.rb +86 -0
  325. data/spec/svg_conform/requirements/namespace_requirement_spec.rb +184 -0
  326. data/spec/svg_conform/requirements/no_external_css_requirement_spec.rb +78 -0
  327. data/spec/svg_conform/requirements/no_external_fonts_requirement_spec.rb +20 -0
  328. data/spec/svg_conform/requirements/no_external_images_requirement_spec.rb +20 -0
  329. data/spec/svg_conform/requirements/style_promotion_requirement_spec.rb +78 -0
  330. data/spec/svg_conform/requirements/style_requirement_spec.rb +76 -0
  331. data/spec/svg_conform/requirements/viewbox_required_requirement_spec.rb +165 -0
  332. data/spec/svg_conform_spec.rb +32 -0
  333. data/spec/svgcheck_compatibility_spec.rb +355 -0
  334. data/svg_conform.gemspec +35 -0
  335. 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