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,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module SvgConform
6
+ # Analyzes compatibility between svg_conform and svgcheck results
7
+ class CompatibilityAnalyzer
8
+ attr_reader :results
9
+
10
+ def initialize
11
+ @results = {}
12
+ end
13
+
14
+ # Analyze all test files and compare results
15
+ def analyze_all_files
16
+ input_dir = File.join(__dir__, "..", "..", "spec", "fixtures",
17
+ "svgcheck", "inputs")
18
+ check_dir = File.join(__dir__, "..", "..", "spec", "fixtures",
19
+ "svgcheck", "check")
20
+ repair_dir = File.join(__dir__, "..", "..", "spec", "fixtures",
21
+ "svgcheck", "repair")
22
+
23
+ Dir.glob(File.join(input_dir, "*.svg")).each do |input_file|
24
+ basename = File.basename(input_file, ".svg")
25
+ next if basename.start_with?("rfc") # Skip non-SVG files
26
+
27
+ puts "Analyzing #{basename}..."
28
+ result = analyze_file(input_file, check_dir, repair_dir)
29
+ @results[basename] = result
30
+ end
31
+
32
+ @results
33
+ end
34
+
35
+ # Analyze a single file
36
+ def analyze_file(input_file, check_dir, repair_dir)
37
+ basename = File.basename(input_file, ".svg")
38
+
39
+ # Load svgcheck results
40
+ svgcheck_out = load_svgcheck_file(check_dir, "#{basename}.svg.out")
41
+ svgcheck_err = load_svgcheck_file(check_dir, "#{basename}.svg.err")
42
+ svgcheck_code = load_svgcheck_file(check_dir, "#{basename}.svg.code")
43
+ svgcheck_repair = load_svgcheck_file(repair_dir, "#{basename}.svg.file")
44
+
45
+ # Run our validation
46
+ our_validation = run_our_validation(input_file)
47
+ our_remediation = nil # Skip remediation for now
48
+
49
+ # Parse svgcheck results
50
+ svgcheck_errors = parse_svgcheck_errors(svgcheck_out)
51
+ svgcheck_valid = svgcheck_out.include?("INFO: File conforms to SVG requirements")
52
+
53
+ # Compare results
54
+ {
55
+ file: basename,
56
+ input_file: input_file,
57
+ svgcheck: {
58
+ valid: svgcheck_valid,
59
+ errors: svgcheck_errors,
60
+ error_count: svgcheck_errors.length,
61
+ exit_code: svgcheck_code&.strip&.to_i,
62
+ raw_output: svgcheck_out,
63
+ raw_error: svgcheck_err,
64
+ repaired_content: svgcheck_repair,
65
+ },
66
+ our_results: {
67
+ valid: our_validation[:valid],
68
+ errors: our_validation[:errors],
69
+ error_count: our_validation[:errors].length,
70
+ remediated_content: our_remediation,
71
+ },
72
+ compatibility: analyze_compatibility(svgcheck_errors, svgcheck_valid,
73
+ our_validation),
74
+ }
75
+ end
76
+
77
+ # Generate a detailed report
78
+ def generate_report
79
+ return "No analysis results available" if @results.empty?
80
+
81
+ report = []
82
+ report << "SVG Conform vs SVGCheck Compatibility Analysis"
83
+ report << "=" * 50
84
+ report << ""
85
+
86
+ # Summary statistics
87
+ total_files = @results.length
88
+ compatible_files = @results.count do |_, r|
89
+ r[:compatibility][:overall_compatible]
90
+ end
91
+
92
+ report << "Summary:"
93
+ report << " Total files analyzed: #{total_files}"
94
+ report << " Compatible files: #{compatible_files}"
95
+ report << " Compatibility rate: #{(compatible_files.to_f / total_files * 100).round(1)}%"
96
+ report << ""
97
+
98
+ # Detailed analysis for each file
99
+ @results.each do |basename, result|
100
+ report << "File: #{basename}"
101
+ report << "-" * 30
102
+
103
+ # Validation comparison
104
+ svgcheck_valid = result[:svgcheck][:valid]
105
+ our_valid = result[:our_results][:valid]
106
+
107
+ report << " Validation:"
108
+ report << " SVGCheck: #{svgcheck_valid ? 'VALID' : 'INVALID'} (#{result[:svgcheck][:error_count]} errors)"
109
+ report << " Our tool: #{our_valid ? 'VALID' : 'INVALID'} (#{result[:our_results][:error_count]} errors)"
110
+ report << " Match: #{svgcheck_valid == our_valid ? 'YES' : 'NO'}"
111
+
112
+ # Error analysis
113
+ if result[:svgcheck][:error_count].positive? || result[:our_results][:error_count].positive?
114
+ report << " Errors:"
115
+
116
+ if result[:svgcheck][:error_count].positive?
117
+ report << " SVGCheck errors:"
118
+ result[:svgcheck][:errors].each do |error|
119
+ report << " - Line #{error[:line]}: #{error[:message]}"
120
+ end
121
+ end
122
+
123
+ if result[:our_results][:error_count].positive?
124
+ report << " Our errors:"
125
+ result[:our_results][:errors].each do |error|
126
+ report << " - #{error}"
127
+ end
128
+ end
129
+ end
130
+
131
+ # Compatibility issues
132
+ compatibility = result[:compatibility]
133
+ unless compatibility[:issues].empty?
134
+ report << " Compatibility Issues:"
135
+ compatibility[:issues].each do |issue|
136
+ report << " - #{issue}"
137
+ end
138
+ end
139
+
140
+ report << ""
141
+ end
142
+
143
+ # Pattern analysis
144
+ report << "Common Patterns:"
145
+ report << "-" * 20
146
+
147
+ # Find common error types
148
+ all_svgcheck_errors = @results.values.flat_map do |r|
149
+ r[:svgcheck][:errors]
150
+ end
151
+ error_patterns = all_svgcheck_errors.group_by do |e|
152
+ extract_error_pattern(e[:message])
153
+ end
154
+
155
+ error_patterns.each do |pattern, errors|
156
+ report << " #{pattern}: #{errors.length} occurrences"
157
+ end
158
+
159
+ report.join("\n")
160
+ end
161
+
162
+ private
163
+
164
+ def load_svgcheck_file(dir, filename)
165
+ file_path = File.join(dir, filename)
166
+ return nil unless File.exist?(file_path)
167
+
168
+ File.read(file_path)
169
+ rescue StandardError
170
+ nil
171
+ end
172
+
173
+ def run_our_validation(input_file)
174
+ svg_content = File.read(input_file)
175
+ validator = SvgConform::Validator.new
176
+ result = validator.validate(svg_content, profile: :svg_1_2_rfc)
177
+
178
+ {
179
+ valid: result.valid?,
180
+ errors: result.errors.map { |e| "Line #{e.line || '?'}: #{e.message}" },
181
+ }
182
+ rescue StandardError => e
183
+ {
184
+ valid: false,
185
+ errors: ["ERROR: #{e.message}"],
186
+ }
187
+ end
188
+
189
+ def run_our_remediation(input_file)
190
+ document = SvgConform::Document.from_file(input_file)
191
+ profile = SvgConform::Profiles.get(:svg_1_2_rfc)
192
+ fixer = SvgConform::Fixer.new(profile)
193
+
194
+ fixed_document = fixer.fix(document)
195
+ fixed_document.to_xml
196
+ rescue StandardError => e
197
+ "ERROR: #{e.message}"
198
+ end
199
+
200
+ def parse_svgcheck_errors(output)
201
+ return [] if output.nil? || output.empty?
202
+
203
+ errors = []
204
+ output.lines.each do |line|
205
+ line = line.strip
206
+ next if line.empty?
207
+ next if line.start_with?("INFO:")
208
+ next if line.include?("File conforms to SVG requirements")
209
+
210
+ # Parse error format: "filename:line: message"
211
+ if line =~ /^(.+):(\d+): (.+)$/
212
+ errors << {
213
+ file: Regexp.last_match(1),
214
+ line: Regexp.last_match(2).to_i,
215
+ message: Regexp.last_match(3),
216
+ }
217
+ elsif line.start_with?("ERROR:")
218
+ errors << {
219
+ file: "",
220
+ line: 0,
221
+ message: line,
222
+ }
223
+ end
224
+ end
225
+
226
+ errors
227
+ end
228
+
229
+ def analyze_compatibility(svgcheck_errors, svgcheck_valid, our_validation)
230
+ issues = []
231
+
232
+ # Check if validation results match
233
+ if svgcheck_valid != our_validation[:valid]
234
+ if svgcheck_valid && !our_validation[:valid]
235
+ issues << "We reject a file that svgcheck accepts"
236
+ elsif !svgcheck_valid && our_validation[:valid]
237
+ issues << "We accept a file that svgcheck rejects"
238
+ end
239
+ end
240
+
241
+ # Check error count differences
242
+ error_count_diff = (svgcheck_errors.length - our_validation[:errors].length).abs
243
+ issues << "Error count differs by #{error_count_diff}" if error_count_diff.positive?
244
+
245
+ # Check for missing error types
246
+ svgcheck_error_types = svgcheck_errors.map do |e|
247
+ extract_error_pattern(e[:message])
248
+ end.uniq
249
+ our_error_types = our_validation[:errors].map do |e|
250
+ extract_error_pattern(e)
251
+ end.uniq
252
+
253
+ missing_types = svgcheck_error_types - our_error_types
254
+ extra_types = our_error_types - svgcheck_error_types
255
+
256
+ missing_types.each { |type| issues << "Missing error type: #{type}" }
257
+ extra_types.each { |type| issues << "Extra error type: #{type}" }
258
+
259
+ {
260
+ overall_compatible: issues.empty?,
261
+ issues: issues,
262
+ error_count_match: svgcheck_errors.length == our_validation[:errors].length,
263
+ validation_result_match: svgcheck_valid == our_validation[:valid],
264
+ }
265
+ end
266
+
267
+ def extract_error_pattern(error_message)
268
+ # Extract the general pattern from an error message
269
+ case error_message
270
+ when /attribute '(\w+)' does not allow the value '([^']+)'/
271
+ "invalid_attribute_value:#{Regexp.last_match(1)}"
272
+ when /Color '([^']+)' in attribute '(\w+)'/
273
+ "invalid_color:#{Regexp.last_match(2)}"
274
+ when /viewBox/i
275
+ "viewbox_issue"
276
+ when /namespace/i
277
+ "namespace_issue"
278
+ when /font/i
279
+ "font_issue"
280
+ else
281
+ error_message.split(":").first || error_message
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module SvgConform
6
+ # Individual error/warning entry in the report
7
+ class ConformanceIssue < Lutaml::Model::Serializable
8
+ attribute :type, :string
9
+ attribute :requirement_id, :string
10
+ attribute :message, :string
11
+ attribute :element, :string
12
+ attribute :attribute, :string
13
+ attribute :value, :string
14
+ attribute :line, :integer
15
+ attribute :column, :integer
16
+ attribute :xpath, :string
17
+ attribute :severity, :string
18
+ attribute :category, :string
19
+ attribute :remediation_available, :boolean, default: -> { false }
20
+ attribute :context, :hash, default: -> { {} }
21
+
22
+ yaml do
23
+ map "type", to: :type
24
+ map "requirement_id", to: :requirement_id
25
+ map "message", to: :message
26
+ map "element", to: :element
27
+ map "attribute", to: :attribute
28
+ map "value", to: :value
29
+ map "line", to: :line
30
+ map "column", to: :column
31
+ map "xpath", to: :xpath
32
+ map "severity", to: :severity
33
+ map "category", to: :category
34
+ map "remediation_available", to: :remediation_available
35
+ map "context", to: :context
36
+ end
37
+ end
38
+
39
+ # Summary of issues by type
40
+ class IssueSummary < Lutaml::Model::Serializable
41
+ attribute :total_count, :integer
42
+ attribute :by_requirement, :hash, default: -> { {} }
43
+ attribute :issues, ConformanceIssue, collection: true, default: -> { [] }
44
+
45
+ yaml do
46
+ map "total_count", to: :total_count
47
+ map "by_requirement", to: :by_requirement
48
+ map "issues", to: :issues
49
+ end
50
+ end
51
+
52
+ # Main conformance report structure
53
+ class ConformanceReport < Lutaml::Model::Serializable
54
+ attribute :filename, :string
55
+ attribute :profile, :string
56
+ attribute :tool, :string # "svg_conform" or "svgcheck"
57
+ attribute :version, :string
58
+ attribute :timestamp, :string
59
+ attribute :valid, :boolean
60
+ attribute :errors, IssueSummary
61
+ attribute :warnings, IssueSummary
62
+
63
+ yaml do
64
+ map "filename", to: :filename
65
+ map "profile", to: :profile
66
+ map "tool", to: :tool
67
+ map "version", to: :version
68
+ map "timestamp", to: :timestamp
69
+ map "valid", to: :valid
70
+ map "errors", to: :errors
71
+ map "warnings", to: :warnings
72
+ end
73
+
74
+ # Create report from SvgConform ValidationResult
75
+ def self.from_svg_conform_result(filename, validation_result, profile: nil,
76
+ use_svgcheck_mapping: false)
77
+ report = new
78
+ report.filename = filename
79
+ report.profile = profile
80
+ report.tool = "svg_conform"
81
+ report.version = SvgConform::VERSION
82
+ report.timestamp = Time.now.iso8601
83
+ report.valid = validation_result.valid?
84
+ report.errors = IssueSummary.new
85
+ report.errors.total_count = 0
86
+ report.errors.by_requirement = {}
87
+ report.errors.issues = []
88
+
89
+ report.warnings = IssueSummary.new
90
+ report.warnings.total_count = 0
91
+ report.warnings.by_requirement = {}
92
+ report.warnings.issues = []
93
+
94
+ # Use SvgcheckCompatibilityEngine for svgcheck compatibility
95
+ if use_svgcheck_mapping
96
+ require_relative "external_checkers/svgcheck/compatibility_engine"
97
+ compatibility_engine = ExternalCheckers::Svgcheck::CompatibilityEngine.new
98
+
99
+ # Check if file should be treated as unparseable by svgcheck
100
+ if compatibility_engine.should_mimic_parse_failure?(filename,
101
+ validation_result)
102
+ # Return empty report like svgcheck does for unparseable files
103
+ return report
104
+ end
105
+ end
106
+
107
+ # Process errors (including conditional validity_errors for svgcheck compatibility)
108
+ all_errors = validation_result.errors.dup
109
+
110
+ # For svgcheck compatibility: include validity_errors as regular errors
111
+ if use_svgcheck_mapping && validation_result.validity_errors.any?
112
+ compatibility_engine ||= ExternalCheckers::Svgcheck::CompatibilityEngine.new
113
+ all_errors.concat(validation_result.validity_errors) if compatibility_engine.should_include_validity_errors?(
114
+ validation_result, filename
115
+ )
116
+ end
117
+
118
+ if all_errors.any?
119
+ # Filter errors using compatibility engine or use direct mapping
120
+ filtered_errors = if use_svgcheck_mapping
121
+ compatibility_engine ||= ExternalCheckers::Svgcheck::CompatibilityEngine.new
122
+ compatibility_engine.filter_errors_for_svgcheck(
123
+ all_errors, filename, validation_result
124
+ )
125
+ else
126
+ all_errors.map do |error|
127
+ [error, error.requirement_id]
128
+ end
129
+ end
130
+
131
+ error_groups = filtered_errors.group_by { |_error, req_id| req_id }
132
+
133
+ error_groups.each do |req_id, error_pairs|
134
+ report.errors.by_requirement[req_id] = error_pairs.length
135
+ end
136
+
137
+ # Update total count to reflect filtered errors
138
+ report.errors.total_count = filtered_errors.length
139
+
140
+ # Add ALL filtered errors (no sampling)
141
+ report.errors.issues = filtered_errors.map do |error, mapped_req_id|
142
+ # Extract attribute and value from error data if available
143
+ attribute = error.data[:attribute] if error.respond_to?(:data) && error.data
144
+ value = error.data[:value] if error.respond_to?(:data) && error.data
145
+
146
+ issue = ConformanceIssue.new
147
+ issue.type = "error"
148
+ issue.requirement_id = mapped_req_id
149
+ issue.message = error.message
150
+ issue.element = error.element_name
151
+ issue.attribute = attribute
152
+ issue.value = value
153
+ issue.line = error.line
154
+ issue
155
+ end
156
+ end
157
+
158
+ # Process warnings
159
+ if validation_result.warnings.any?
160
+ report.warnings.total_count = validation_result.warnings.length
161
+
162
+ # Group by requirement_id
163
+ warning_groups = validation_result.warnings.group_by(&:requirement_id)
164
+ warning_groups.each do |req_id, warnings|
165
+ report.warnings.by_requirement[req_id] = warnings.length
166
+ end
167
+
168
+ # Add ALL warnings (no sampling)
169
+ report.warnings.issues = validation_result.warnings.map do |warning|
170
+ issue = ConformanceIssue.new
171
+ issue.type = "warning"
172
+ issue.requirement_id = warning.requirement_id
173
+ issue.message = warning.message
174
+ issue.element = warning.element_name
175
+ issue.line = warning.line
176
+ issue
177
+ end
178
+ end
179
+
180
+ report
181
+ end
182
+
183
+ # Create report from svgcheck error output
184
+ def self.from_svgcheck_result(filename, error_content,
185
+ _output_content = nil)
186
+ report = new
187
+ report.filename = filename
188
+ report.tool = "svgcheck"
189
+ report.timestamp = Time.now.iso8601
190
+ report.valid = error_content.strip.empty?
191
+ report.errors = IssueSummary.new
192
+ report.errors.total_count = 0
193
+ report.errors.by_requirement = {}
194
+ report.errors.issues = []
195
+
196
+ report.warnings = IssueSummary.new
197
+ report.warnings.total_count = 0
198
+ report.warnings.by_requirement = {}
199
+ report.warnings.issues = []
200
+
201
+ return report if error_content.strip.empty?
202
+
203
+ # Parse svgcheck errors using the dedicated parser
204
+ require_relative "external_checkers/svgcheck/parser"
205
+ parser = ExternalCheckers::Svgcheck::Parser.new
206
+ parsed_report = parser.parse(error_content, nil, filename: filename)
207
+ errors = parsed_report.errors.issues
208
+
209
+ if errors.any?
210
+ report.errors.total_count = errors.length
211
+ report.valid = false
212
+
213
+ # Group by type for summary
214
+ error_groups = errors.group_by { |e| e.requirement_id || "unknown" }
215
+ error_groups.each do |type, type_errors|
216
+ report.errors.by_requirement[type] = type_errors.length
217
+ end
218
+
219
+ # Add ALL errors (no sampling)
220
+ report.errors.issues = errors
221
+ end
222
+
223
+ report
224
+ end
225
+
226
+ # Compare two conformance reports
227
+ def compare_with(other_report)
228
+ differences = []
229
+
230
+ # Compare error counts
231
+ differences << "Error count: #{errors.total_count} vs #{other_report.errors.total_count}" if errors.total_count != other_report.errors.total_count
232
+
233
+ # Compare warning counts
234
+ differences << "Warning count: #{warnings.total_count} vs #{other_report.warnings.total_count}" if warnings.total_count != other_report.warnings.total_count
235
+
236
+ # Compare error types
237
+ all_req_ids = (errors.by_requirement.keys + other_report.errors.by_requirement.keys).uniq
238
+ all_req_ids.each do |req_id|
239
+ our_count = errors.by_requirement[req_id] || 0
240
+ their_count = other_report.errors.by_requirement[req_id] || 0
241
+
242
+ differences << "#{req_id} errors: #{our_count} vs #{their_count}" if our_count != their_count
243
+ end
244
+
245
+ {
246
+ identical: differences.empty?,
247
+ differences: differences,
248
+ summary: differences.empty? ? "Reports are identical" : "#{differences.length} differences found",
249
+ }
250
+ end
251
+
252
+ # Save report to YAML file
253
+ def save_to_file(filepath)
254
+ File.write(filepath, to_yaml)
255
+ end
256
+
257
+ # Load report from YAML file
258
+ def self.load_from_file(filepath)
259
+ from_yaml(File.read(filepath))
260
+ end
261
+
262
+ # Check if the report indicates a valid SVG
263
+ def valid?
264
+ valid
265
+ end
266
+ end
267
+ end