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
data/examples/demo.rb ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Demo script showing SvgConform capabilities
5
+ require_relative "../lib/svg_conform"
6
+
7
+ puts "SvgConform Demo"
8
+ puts "=" * 50
9
+
10
+ # Test with actual fixture files
11
+ fixtures_dir = File.join(__dir__, "..", "spec", "fixtures", "svgcheck",
12
+ "inputs")
13
+ test_files = ["viewBox-height.svg", "colors.svg", "IETF-test.svg", "good.svg"]
14
+
15
+ validator = SvgConform::Validator.new
16
+
17
+ # Show available profiles
18
+ puts "\n1. Available Profiles:"
19
+ puts "-" * 30
20
+
21
+ SvgConform::Profiles.available_profiles.each do |name|
22
+ profile = SvgConform::Profiles.get(name)
23
+ puts "#{name}: #{profile.description} (#{profile.requirement_count} requirements, #{profile.remediation_count} remediations)"
24
+ end
25
+
26
+ # Test each profile with sample files
27
+ test_files.each do |filename|
28
+ file_path = File.join(fixtures_dir, filename)
29
+ next unless File.exist?(file_path)
30
+
31
+ puts "\n#{'=' * 60}"
32
+ puts "Testing file: #{filename}"
33
+ puts "=" * 60
34
+
35
+ # Read and display the SVG content
36
+ svg_content = File.read(file_path)
37
+ puts "\nSVG Content:"
38
+ puts svg_content
39
+ puts
40
+
41
+ # Test each profile
42
+ SvgConform::Profiles.available_profiles.each do |profile_name|
43
+ puts "\n#{profile_name.to_s.upcase} Profile Results:"
44
+ puts "-" * 40
45
+
46
+ begin
47
+ result = validator.validate_file(file_path, profile: profile_name)
48
+
49
+ puts "Status: #{result.valid? ? 'VALID ✓' : 'INVALID ✗'}"
50
+ puts "Errors: #{result.error_count}"
51
+ puts "Warnings: #{result.warning_count}"
52
+
53
+ if result.has_errors?
54
+ puts "\nErrors:"
55
+ result.errors.each_with_index do |error, i|
56
+ puts " #{i + 1}. #{error.message} (#{error.requirement_id})"
57
+ end
58
+ end
59
+
60
+ if result.has_warnings?
61
+ puts "\nWarnings:"
62
+ result.warnings.each_with_index do |warning, i|
63
+ puts " #{i + 1}. #{warning.message} (#{warning.requirement_id})"
64
+ end
65
+ end
66
+
67
+ # Test remediation if available
68
+ if result.fixable?
69
+ puts "\nApplying fixes (#{result.fixable_count} available)..."
70
+
71
+ # Apply fixes using the ValidationResult's built-in fix method
72
+ result.apply_fixes
73
+
74
+ if result.fixed?
75
+ puts "✓ Fixes applied successfully!"
76
+ puts "Fixed SVG preview (first 200 chars):"
77
+ fixed_content = result.fixed_document.to_xml
78
+ puts fixed_content[0..200] + (fixed_content.length > 200 ? "..." : "")
79
+
80
+ # Re-validate the fixed document
81
+ fixed_result = validator.validate(fixed_content,
82
+ profile: profile_name)
83
+ puts "\nRe-validation: #{fixed_result.valid? ? 'VALID ✓' : 'INVALID ✗'}"
84
+ puts "Remaining errors: #{fixed_result.error_count}"
85
+ else
86
+ puts "✗ Could not apply fixes"
87
+ end
88
+ end
89
+ rescue StandardError => e
90
+ puts "ERROR: #{e.message}"
91
+ end
92
+ end
93
+ end
94
+
95
+ # Performance test
96
+ puts "\n#{'=' * 60}"
97
+ puts "Performance Test"
98
+ puts "=" * 60
99
+
100
+ require "benchmark"
101
+ test_file = File.join(fixtures_dir, "good.svg")
102
+
103
+ if File.exist?(test_file)
104
+ time = Benchmark.realtime do
105
+ 100.times do
106
+ validator.validate_file(test_file, profile: :svg_1_2_rfc)
107
+ end
108
+ end
109
+
110
+ puts "Validated #{test_file} 100 times in #{time.round(3)} seconds"
111
+ puts "Average: #{(time * 1000 / 100).round(2)}ms per validation"
112
+ else
113
+ puts "Performance test skipped - test file not found"
114
+ end
115
+
116
+ puts "\nDemo completed!"
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/svg_conform"
5
+
6
+ # Demo script showing the new requirements and remediations system
7
+ class RequirementsDemo
8
+ def run
9
+ puts "=== SvgConform Requirements & Remediations Demo ==="
10
+ puts
11
+
12
+ # Demo 1: Load profile from YAML
13
+ demo_yaml_profile_loading
14
+
15
+ # Demo 2: Validate with requirements
16
+ demo_requirements_validation
17
+
18
+ # Demo 3: Apply remediations
19
+ demo_remediations
20
+
21
+ # Demo 4: Lucid fix scenario
22
+ demo_lucid_fix
23
+ end
24
+
25
+ private
26
+
27
+ def demo_yaml_profile_loading
28
+ puts "1. Loading Profiles from YAML Configuration"
29
+ puts "=" * 50
30
+
31
+ begin
32
+ # Load IETF profile from YAML
33
+ svg_1_2_rfc_profile = SvgConform::ProfileRegistry.load_profile("svg_1_2_rfc")
34
+ puts "✓ Loaded IETF profile: #{svg_1_2_rfc_profile.name}"
35
+ puts " Description: #{svg_1_2_rfc_profile.description}"
36
+ puts " Requirements: #{svg_1_2_rfc_profile.requirement_count}"
37
+ puts " Remediations: #{svg_1_2_rfc_profile.remediation_count}"
38
+ puts
39
+
40
+ # Load Lucid fix profile
41
+ lucid_profile = SvgConform::ProfileRegistry.load_profile("lucid_fix")
42
+ puts "✓ Loaded Lucid Fix profile: #{lucid_profile.name}"
43
+ puts " Description: #{lucid_profile.description}"
44
+ puts " Requirements: #{lucid_profile.requirement_count}"
45
+ puts " Remediations: #{lucid_profile.remediation_count}"
46
+ puts
47
+
48
+ # List available profiles
49
+ available = SvgConform::ProfileRegistry.available_profiles
50
+ puts "Available profiles: #{available.join(', ')}"
51
+ puts
52
+ rescue StandardError => e
53
+ puts "✗ Error loading profiles: #{e.message}"
54
+ puts " This is expected if the requirement classes don't exist yet"
55
+ puts
56
+ end
57
+ end
58
+
59
+ def demo_requirements_validation
60
+ puts "2. Requirements-based Validation"
61
+ puts "=" * 50
62
+
63
+ # Create a test SVG with invalid use element
64
+ invalid_svg = <<~SVG
65
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
66
+ <defs>
67
+ <circle id="valid-circle" cx="10" cy="10" r="5"/>
68
+ </defs>
69
+ <use href="#invalid-id"/>
70
+ <use href="#valid-circle"/>
71
+ <rect fill="red" width="50" height="50"/>
72
+ </svg>
73
+ SVG
74
+
75
+ puts "Test SVG with invalid use element:"
76
+ puts invalid_svg
77
+ puts
78
+
79
+ # Create a simple profile with requirements
80
+ profile = SvgConform::Profile.new(:test_requirements,
81
+ "Test Requirements Profile")
82
+
83
+ begin
84
+ # Add invalid ID references requirement
85
+ requirement = SvgConform::Requirements::InvalidIdReferencesRequirement.new
86
+ profile.add_requirement(requirement)
87
+
88
+ # Validate
89
+ document = SvgConform::Document.new(invalid_svg)
90
+ result = profile.validate(document)
91
+
92
+ puts "Validation Results:"
93
+ puts " Valid: #{result.valid?}"
94
+ puts " Errors: #{result.error_count}"
95
+ puts " Warnings: #{result.warning_count}"
96
+ puts
97
+
98
+ if result.errors.any?
99
+ puts "Errors found:"
100
+ result.errors.each_with_index do |error, i|
101
+ puts " #{i + 1}. #{error.message}"
102
+ puts " Node: #{error.node.name}" if error.node
103
+ puts " Data: #{error.data}" if error.data.any?
104
+ end
105
+ puts
106
+ end
107
+ rescue StandardError => e
108
+ puts "✗ Error during validation: #{e.message}"
109
+ puts " This is expected if the requirement classes don't exist yet"
110
+ puts
111
+ end
112
+ end
113
+
114
+ def demo_remediations
115
+ puts "3. Applying Remediations"
116
+ puts "=" * 50
117
+
118
+ # Create test SVG with issues
119
+ problematic_svg = <<~SVG
120
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
121
+ <defs>
122
+ <circle id="circle1" cx="10" cy="10" r="5"/>
123
+ </defs>
124
+ <use href="#missing-id"/>
125
+ <use href="#circle1"/>
126
+ <use href="#another-missing"/>
127
+ </svg>
128
+ SVG
129
+
130
+ puts "Original SVG with invalid use elements:"
131
+ puts problematic_svg
132
+ puts
133
+
134
+ begin
135
+ # Create profile with requirements and remediations
136
+ profile = SvgConform::Profile.new(:test_remediations,
137
+ "Test Remediations Profile")
138
+
139
+ # Add requirement
140
+ requirement = SvgConform::Requirements::InvalidIdReferencesRequirement.new
141
+ profile.add_requirement(requirement)
142
+
143
+ # Add remediation
144
+ remediation = SvgConform::Remediations::InvalidIdReferencesRemediation.new
145
+ profile.add_remediation(remediation)
146
+
147
+ # Validate first
148
+ document = SvgConform::Document.new(problematic_svg)
149
+ result = profile.validate(document)
150
+
151
+ puts "Validation found #{result.error_count} errors"
152
+
153
+ if result.errors.any?
154
+ # Debug: Show requirement IDs
155
+ puts "Failed requirement IDs: #{result.failed_requirements.map(&:requirement_id)}"
156
+ puts "Available remediations: #{profile.remediations.map do |r|
157
+ "#{r.id} (targets: #{r.target_requirements})"
158
+ end}"
159
+ puts
160
+
161
+ # Apply remediations
162
+ engine = SvgConform::RemediationEngine.new(profile)
163
+ remediation_results = engine.apply_remediations(document, result)
164
+
165
+ puts "Applied #{remediation_results.size} remediations"
166
+ puts "Summary: #{engine.summary}"
167
+ puts
168
+
169
+ puts "Fixed SVG:"
170
+ puts document.to_xml
171
+ puts
172
+ end
173
+ rescue StandardError => e
174
+ puts "✗ Error during remediation: #{e.message}"
175
+ puts " This is expected if the remediation classes don't exist yet"
176
+ puts
177
+ end
178
+ end
179
+
180
+ def demo_lucid_fix
181
+ puts "4. Lucid SVG Fix Scenario"
182
+ puts "=" * 50
183
+
184
+ # Simulate a Lucid-generated SVG with invalid use elements
185
+ lucid_svg = <<~SVG
186
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 100">
187
+ <defs>
188
+ <g id="shape1">
189
+ <rect width="20" height="20" fill="blue"/>
190
+ </g>
191
+ </defs>
192
+ <g>
193
+ <use xlink:href="#shape1" x="10" y="10"/>
194
+ <use xlink:href="#nonexistent1" x="50" y="10"/>
195
+ <use xlink:href="#nonexistent2" x="90" y="10"/>
196
+ <text x="10" y="50">Valid content</text>
197
+ </g>
198
+ </svg>
199
+ SVG
200
+
201
+ puts "Lucid-style SVG with invalid use elements:"
202
+ puts lucid_svg
203
+ puts
204
+
205
+ begin
206
+ # Load the Lucid fix profile
207
+ profile = SvgConform::ProfileRegistry.load_profile("lucid_fix")
208
+
209
+ # Process the SVG
210
+ document = SvgConform::Document.new(lucid_svg)
211
+ result = profile.validate(document)
212
+
213
+ puts "Found #{result.error_count} invalid use elements"
214
+
215
+ if result.errors.any?
216
+ # Apply the Lucid fix
217
+ engine = SvgConform::RemediationEngine.new(profile)
218
+ engine.apply_remediations(document, result)
219
+
220
+ puts "Applied Lucid fix remediations"
221
+ puts "Summary: #{engine.summary}"
222
+ puts
223
+
224
+ puts "Fixed SVG (invalid use elements removed):"
225
+ puts document.to_xml
226
+ puts
227
+ end
228
+ rescue StandardError => e
229
+ puts "✗ Error during Lucid fix: #{e.message}"
230
+ puts " This is expected if the profile/classes don't exist yet"
231
+ puts
232
+ end
233
+ end
234
+ end
235
+
236
+ # Run the demo
237
+ if __FILE__ == $PROGRAM_NAME
238
+ demo = RequirementsDemo.new
239
+ demo.run
240
+ end
data/exe/svg_conform ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/svg_conform"
5
+
6
+ # Run the Thor CLI
7
+ SvgConform::Cli.start(ARGV)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module SvgConform
6
+ # Result for a single file in batch processing
7
+ class FileResult < Lutaml::Model::Serializable
8
+ attribute :filename, :string
9
+ attribute :original_path, :string
10
+ attribute :valid_before, :boolean
11
+ attribute :valid_after, :boolean
12
+ attribute :errors_before, :integer
13
+ attribute :errors_after, :integer
14
+ attribute :remediated_path, :string
15
+ attribute :status, :string # "valid", "remediated", "failed", "error"
16
+ attribute :error_message, :string
17
+
18
+ yaml do
19
+ map "filename", to: :filename
20
+ map "original_path", to: :original_path
21
+ map "valid_before", to: :valid_before
22
+ map "valid_after", to: :valid_after
23
+ map "errors_before", to: :errors_before
24
+ map "errors_after", to: :errors_after
25
+ map "remediated_path", to: :remediated_path
26
+ map "status", to: :status
27
+ map "error_message", to: :error_message
28
+ end
29
+ end
30
+
31
+ # Batch validation/remediation report
32
+ class BatchReport < Lutaml::Model::Serializable
33
+ attribute :directory, :string
34
+ attribute :profile, :string
35
+ attribute :timestamp, :string, default: -> { Time.now.iso8601 }
36
+ attribute :total_files, :integer
37
+ attribute :valid_before, :integer
38
+ attribute :valid_after, :integer
39
+ attribute :remediated, :integer
40
+ attribute :failed, :integer
41
+ attribute :success_rate, :float
42
+ attribute :files, FileResult, collection: true, default: []
43
+ attribute :manifest, :hash, default: {}
44
+
45
+ yaml do
46
+ map "directory", to: :directory
47
+ map "profile", to: :profile
48
+ map "timestamp", to: :timestamp
49
+ map "total_files", to: :total_files
50
+ map "valid_before", to: :valid_before
51
+ map "valid_after", to: :valid_after
52
+ map "remediated", to: :remediated
53
+ map "failed", to: :failed
54
+ map "success_rate", to: :success_rate
55
+ map "files", to: :files
56
+ map "manifest", to: :manifest
57
+ end
58
+
59
+ def calculate_statistics
60
+ self.total_files = files.length
61
+ self.valid_before = files.count(&:valid_before)
62
+ self.valid_after = files.count(&:valid_after)
63
+ self.remediated = files.count { |f| f.status == "remediated" }
64
+ self.failed = files.count do |f|
65
+ ["failed", "error"].include?(f.status)
66
+ end
67
+ self.success_rate = total_files.zero? ? 0.0 : (valid_after.to_f / total_files * 100).round(1)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "json"
5
+ require "yaml"
6
+ require "paint"
7
+ require_relative "commands/check"
8
+ require_relative "commands/svgcheck"
9
+ require_relative "commands/profiles"
10
+
11
+ module SvgConform
12
+ # Thor-based CLI for SvgConform with svgcheck-like functionality
13
+ class Cli < Thor
14
+ def initialize(*args)
15
+ super
16
+ end
17
+
18
+ desc "check [*FILES]",
19
+ "Check SVG file(s) validity (single file, multiple files, or directory)"
20
+ long_desc <<~DESC
21
+ Check SVG file validity against a profile.
22
+
23
+ MODES:
24
+ 1. Single file: svg_conform check file.svg -p PROFILE
25
+ 2. Multiple files: svg_conform check *.svg -p PROFILE
26
+ 3. Directory: svg_conform check --directory PATH -p PROFILE
27
+
28
+ By default, only validates without creating remediated files.
29
+ Use --fix to enable automatic remediation.
30
+
31
+ REMEDIATION OUTPUT:
32
+ - Single file: Uses --fix-output (default: FILE.fixed.svg)
33
+ - Multiple files: Requires --output-dir or --in-place
34
+ - Directory: Requires --output-dir or --in-place
35
+
36
+ REPORT FORMATS:
37
+ - table: Human-readable (default for single file)
38
+ - json: JSON format using lutaml-model
39
+ - yaml: YAML format using lutaml-model
40
+
41
+ EXAMPLES:
42
+ svg_conform check file.svg -p metanorma
43
+ svg_conform check *.svg -p metanorma -f --output-dir fixed/
44
+ svg_conform check -d images/ -p metanorma --report-format json --report-output report.json
45
+ svg_conform check -d images/ -p metanorma -f --in-place --force
46
+ DESC
47
+ option :directory, aliases: "-d", type: :string,
48
+ desc: "Directory to scan recursively for SVG files"
49
+ option :profile, aliases: "-p", default: "svg_1_2_rfc",
50
+ desc: "Profile to validate against"
51
+ option :format, default: "table", enum: %w[table yaml json],
52
+ desc: "Output format (single file mode)"
53
+ option :fix, aliases: "-f", type: :boolean, default: false,
54
+ desc: "Create remediated files"
55
+ option :output, aliases: "-o", desc: "Output file (single file mode)"
56
+ option :output_dir, type: :string,
57
+ desc: "Output directory for remediated files (multi-file mode)"
58
+ option :in_place, type: :boolean, default: false,
59
+ desc: "Replace original files (requires --force)"
60
+ option :force, type: :boolean, default: false,
61
+ desc: "Confirm destructive operations (required for --in-place)"
62
+ option :fix_output,
63
+ desc: "Output file for remediated SVG (single file mode, default: FILE.fixed.svg)"
64
+ option :report_format, enum: %w[json yaml],
65
+ desc: "Batch report format (json or yaml)"
66
+ option :report_output, type: :string,
67
+ desc: "Save detailed batch report to file"
68
+ option :manifest, type: :string,
69
+ desc: "Manifest file path (default: manifest.json with --fix)"
70
+ option :quiet, aliases: "-q", type: :boolean, default: false,
71
+ desc: "Suppress per-file output, show summary only"
72
+ option :verbose, aliases: "-v", type: :boolean, default: false,
73
+ desc: "Show detailed progress"
74
+ def check(*files)
75
+ SvgConform::Commands::Check.new(files, options).execute
76
+ end
77
+
78
+ desc "svgcheck SUBCOMMAND",
79
+ "Svgcheck-related commands (compare, compatibility, generate)"
80
+ subcommand "svgcheck", Commands::Svgcheck
81
+
82
+ desc "profiles", "List available validation profiles"
83
+ long_desc <<~DESC
84
+ List all available validation profiles with their descriptions
85
+ and requirements.
86
+ DESC
87
+ option :verbose, aliases: "-v", type: :boolean, default: false,
88
+ desc: "Show detailed information"
89
+ def profiles
90
+ SvgConform::Commands::Profiles.new(options).execute
91
+ end
92
+
93
+ desc "version", "Show version information"
94
+ def version
95
+ puts "SvgConform #{SvgConform::VERSION}"
96
+ end
97
+
98
+ private
99
+
100
+ def method_missing(method_name, *_args)
101
+ puts Paint["Unknown command: #{method_name}", :red]
102
+ puts
103
+ help
104
+ exit 1
105
+ end
106
+ end
107
+ end