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,408 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module SvgConform
6
+ # Context object passed to rules during validation
7
+ class ValidationContext
8
+ attr_reader :document, :profile, :errors, :warnings, :fixes,
9
+ :validity_errors
10
+
11
+ def initialize(document, profile)
12
+ @document = document
13
+ @profile = profile
14
+ @errors = []
15
+ @warnings = []
16
+ @validity_errors = []
17
+ @infos = []
18
+ @data = {}
19
+ @structurally_invalid_node_ids = Set.new
20
+ end
21
+
22
+ # Mark a node as structurally invalid (e.g., invalid parent-child relationship)
23
+ # Other requirements should skip attribute validation on these nodes
24
+ # Also marks all descendants as invalid since they'll be removed with the parent
25
+ def mark_node_structurally_invalid(node)
26
+ node_id = generate_node_id(node)
27
+ @structurally_invalid_node_ids.add(node_id)
28
+
29
+ # Mark all descendants as invalid too
30
+ mark_descendants_invalid(node)
31
+ end
32
+
33
+ # Mark all descendants of a node as structurally invalid
34
+ def mark_descendants_invalid(node)
35
+ return unless node.respond_to?(:children)
36
+
37
+ node.children.each do |child|
38
+ child_id = generate_node_id(child)
39
+ @structurally_invalid_node_ids.add(child_id)
40
+ # Recursively mark descendants
41
+ mark_descendants_invalid(child)
42
+ end
43
+ end
44
+
45
+ # Check if a node is structurally invalid
46
+ def node_structurally_invalid?(node)
47
+ node_id = generate_node_id(node)
48
+ @structurally_invalid_node_ids.include?(node_id)
49
+ end
50
+
51
+ def add_error(node:, message:, rule: nil, requirement: nil,
52
+ requirement_id: nil, severity: nil, fix: nil, data: {})
53
+ # Support both old rule system and new requirements system
54
+ rule_or_requirement = requirement || rule
55
+
56
+ error = ValidationIssue.new(
57
+ type: :error,
58
+ rule: rule_or_requirement,
59
+ node: node,
60
+ message: message,
61
+ fix: fix,
62
+ data: data,
63
+ requirement_id: requirement_id,
64
+ severity: severity,
65
+ )
66
+
67
+ # Handle special severity types
68
+ if severity == :validity_error
69
+ @validity_errors << error
70
+ else
71
+ @errors << error
72
+ end
73
+
74
+ error
75
+ end
76
+
77
+ def add_warning(rule:, node:, message:, fix: nil)
78
+ warning = ValidationIssue.new(
79
+ type: :warning,
80
+ rule: rule,
81
+ node: node,
82
+ message: message,
83
+ fix: fix,
84
+ )
85
+ @warnings << warning
86
+ warning
87
+ end
88
+
89
+ def add_fix(fix)
90
+ @fixes << fix
91
+ end
92
+
93
+ def has_errors?
94
+ !@errors.empty?
95
+ end
96
+
97
+ def has_warnings?
98
+ !@warnings.empty?
99
+ end
100
+
101
+ def has_fixes?
102
+ !@fixes.empty?
103
+ end
104
+
105
+ def issue_count
106
+ @errors.size + @warnings.size
107
+ end
108
+
109
+ def fixable_count
110
+ (@errors + @warnings).count(&:fixable?)
111
+ end
112
+
113
+ def set_data(key, value)
114
+ @data[key] = value
115
+ end
116
+
117
+ def get_data(key)
118
+ @data[key]
119
+ end
120
+
121
+ # Generate a unique identifier for a node based on its path
122
+ # Builds a stable path by walking up the parent chain
123
+ def generate_node_id(node)
124
+ return nil unless node.respond_to?(:name)
125
+
126
+ # Build path by walking up parent chain
127
+ path_parts = []
128
+ current = node
129
+
130
+ while current
131
+ if current.respond_to?(:name) && current.name
132
+ # Count previous siblings of the same type for position
133
+ position = 1
134
+ if current.respond_to?(:previous_sibling)
135
+ sibling = current.previous_sibling
136
+ while sibling
137
+ position += 1 if sibling.respond_to?(:name) && sibling.name == current.name
138
+ sibling = sibling.previous_sibling if sibling.respond_to?(:previous_sibling)
139
+ end
140
+ end
141
+
142
+ path_parts.unshift("#{current.name}[#{position}]")
143
+ end
144
+
145
+ # Stop if we reach the document root (doesn't have parent)
146
+ break unless current.respond_to?(:parent)
147
+
148
+ begin
149
+ current = current.parent
150
+ rescue NoMethodError
151
+ # Parent method failed, we're at root
152
+ break
153
+ end
154
+
155
+ break unless current
156
+ end
157
+
158
+ "/#{path_parts.join('/')}"
159
+ end
160
+ end
161
+
162
+ # Base class for validation issues
163
+ class ValidationIssue
164
+ attr_reader :type, :rule, :node, :message, :fix, :data,
165
+ :requirement_id_override, :severity
166
+
167
+ def initialize(type:, rule:, node:, message:, fix: nil, data: {},
168
+ requirement_id: nil, severity: nil, violation_type: nil)
169
+ @type = type
170
+ @rule = rule
171
+ @node = node
172
+ @message = message
173
+ @fix = fix
174
+ @data = data
175
+ @requirement_id_override = requirement_id
176
+ @severity = severity
177
+ @violation_type = violation_type || detect_violation_type
178
+ end
179
+
180
+ def requirement_id
181
+ return @requirement_id_override.to_s if @requirement_id_override
182
+
183
+ if @rule.respond_to?(:id)
184
+ @rule.id.to_s
185
+ elsif @rule.respond_to?(:class) && @rule.class.respond_to?(:name)
186
+ # Extract ID from class name for requirements
187
+ class_name = @rule.class.name.split("::").last
188
+ class_name.gsub(/Requirement$/, "").downcase.gsub(/([a-z])([A-Z])/,
189
+ '\1_\2').downcase
190
+ else
191
+ "unknown"
192
+ end
193
+ end
194
+
195
+ def error?
196
+ @type == :error
197
+ end
198
+
199
+ def warning?
200
+ @type == :warning
201
+ end
202
+
203
+ def fixable?
204
+ !@fix.nil?
205
+ end
206
+
207
+ def line
208
+ @node.respond_to?(:line) ? @node.line : nil
209
+ end
210
+
211
+ def column
212
+ @node.respond_to?(:column) ? @node.column : nil
213
+ end
214
+
215
+ def element_name
216
+ @node.respond_to?(:name) ? @node.name : nil
217
+ end
218
+
219
+ def apply_fix
220
+ return false unless fixable?
221
+
222
+ begin
223
+ if @fix.respond_to?(:call)
224
+ @fix.call
225
+ else
226
+ @fix.apply
227
+ end
228
+ true
229
+ rescue StandardError
230
+ false
231
+ end
232
+ end
233
+
234
+ def to_h
235
+ {
236
+ type: @type,
237
+ rule: rule_id,
238
+ message: @message,
239
+ line: line,
240
+ column: column,
241
+ element: element_name,
242
+ fixable: fixable?,
243
+ }
244
+ end
245
+
246
+ def violation_type
247
+ @violation_type
248
+ end
249
+
250
+ def remediable?
251
+ case @violation_type
252
+ when :color_violation, :font_violation, :content_violation, :reference_violation,
253
+ :namespace_violation, :viewbox_violation, :style_violation
254
+ true
255
+ when :structural_violation
256
+ false
257
+ else
258
+ # Default to remediable for backward compatibility
259
+ true
260
+ end
261
+ end
262
+
263
+ def remediation_type
264
+ case @violation_type
265
+ when :color_violation, :font_violation
266
+ :convert
267
+ when :content_violation, :reference_violation, :namespace_violation
268
+ :remove
269
+ when :viewbox_violation
270
+ :add
271
+ when :style_violation
272
+ :promote
273
+ else
274
+ :unknown
275
+ end
276
+ end
277
+
278
+ def remediation_confidence
279
+ case @violation_type
280
+ when :color_violation, :font_violation, :style_violation
281
+ :automatic
282
+ when :viewbox_violation, :namespace_violation
283
+ :safe_structural
284
+ when :content_violation, :reference_violation
285
+ :safe_removal
286
+ else
287
+ :manual_review
288
+ end
289
+ end
290
+
291
+ def suggested_action
292
+ case @violation_type
293
+ when :color_violation
294
+ "Convert color to allowed equivalent using CssColor"
295
+ when :font_violation
296
+ "Map font family to generic equivalent"
297
+ when :content_violation
298
+ "Remove forbidden element or attribute"
299
+ when :reference_violation
300
+ "Remove broken reference or containing element"
301
+ when :namespace_violation
302
+ "Fix namespace declarations and remove invalid elements/attributes"
303
+ when :viewbox_violation
304
+ "Add missing viewBox attribute"
305
+ when :style_violation
306
+ "Promote style properties to attributes"
307
+ when :structural_violation
308
+ "Manual fix required - document structure issue"
309
+ else
310
+ "Apply available remediation"
311
+ end
312
+ end
313
+
314
+ def affects_content?
315
+ case @violation_type
316
+ when :content_violation, :reference_violation
317
+ true
318
+ when :color_violation, :font_violation, :style_violation, :viewbox_violation, :namespace_violation
319
+ false
320
+ else
321
+ false
322
+ end
323
+ end
324
+
325
+ def to_s
326
+ location = line ? " at line #{line}" : ""
327
+ location += ":#{column}" if column
328
+ rule_info = rule_id ? " (#{rule_id})" : ""
329
+ remediation_info = remediable? ? " [#{remediation_type}]" : " [NOT REMEDIABLE]"
330
+ "#{@message}#{location}#{rule_info}#{remediation_info}"
331
+ end
332
+
333
+ def to_h
334
+ {
335
+ type: @type,
336
+ rule: rule_id,
337
+ message: @message,
338
+ line: line,
339
+ column: column,
340
+ element: element_name,
341
+ fixable: fixable?,
342
+ remediable: remediable?,
343
+ violation_type: @violation_type,
344
+ remediation_type: remediation_type,
345
+ remediation_confidence: remediation_confidence,
346
+ suggested_action: suggested_action,
347
+ affects_content: affects_content?,
348
+ }
349
+ end
350
+
351
+ private
352
+
353
+ def detect_violation_type
354
+ req_id = requirement_id.downcase
355
+ msg = @message.downcase
356
+
357
+ # First check for structural violations (non-remediable)
358
+ return :structural_violation if msg.include?("root element must be") ||
359
+ msg.include?("malformed") ||
360
+ msg.include?("invalid document") ||
361
+ msg.include?("required element missing") ||
362
+ msg.include?("invalid hierarchy")
363
+
364
+ # Then check requirement-based violations (remediable)
365
+ case req_id
366
+ when /color/
367
+ :color_violation
368
+ when /font/
369
+ :font_violation
370
+ when /forbidden|content/
371
+ :content_violation
372
+ when /reference|id/
373
+ :reference_violation
374
+ when /namespace/
375
+ :namespace_violation
376
+ when /viewbox/
377
+ :viewbox_violation
378
+ when /style/
379
+ :style_violation
380
+ else
381
+ # Check message content for clues
382
+ return :color_violation if msg.include?("color")
383
+ return :font_violation if msg.include?("font")
384
+ return :content_violation if msg.include?("forbidden")
385
+ return :reference_violation if msg.include?("reference") || msg.include?("href")
386
+ return :namespace_violation if msg.include?("namespace")
387
+ return :viewbox_violation if msg.include?("viewbox")
388
+ return :style_violation if msg.include?("style")
389
+
390
+ :unknown_violation
391
+ end
392
+ end
393
+
394
+ def rule_id
395
+ return @requirement_id_override if @requirement_id_override
396
+ return @rule.id if @rule.respond_to?(:id)
397
+
398
+ if @rule.respond_to?(:class) && @rule.class.respond_to?(:name)
399
+ # Extract ID from class name for requirements
400
+ class_name = @rule.class.name.split("::").last
401
+ class_name.gsub(/Requirement$/, "").downcase.gsub(/([a-z])([A-Z])/,
402
+ '\1_\2').downcase
403
+ else
404
+ "unknown"
405
+ end
406
+ end
407
+ end
408
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module SvgConform
6
+ # Represents the result of validating an SVG document
7
+ class ValidationResult
8
+ attr_reader :document, :profile, :errors, :warnings, :validity_errors,
9
+ :fixes_applied, :fixed_document
10
+
11
+ def initialize(document, profile, context)
12
+ @document = document
13
+ @profile = profile
14
+ @errors = context.errors
15
+ @warnings = context.warnings
16
+ @validity_errors = context.validity_errors
17
+ @fixes_applied = []
18
+ @fixed_document = nil
19
+ end
20
+
21
+ def valid?
22
+ @errors.empty? && @validity_errors.empty?
23
+ end
24
+
25
+ def has_errors?
26
+ !@errors.empty?
27
+ end
28
+
29
+ def has_warnings?
30
+ !@warnings.empty?
31
+ end
32
+
33
+ def fixable?
34
+ (@errors + @warnings).any?(&:fixable?)
35
+ end
36
+
37
+ def fixed?
38
+ !@fixed_document.nil?
39
+ end
40
+
41
+ def error_count
42
+ @errors.size
43
+ end
44
+
45
+ def warning_count
46
+ @warnings.size
47
+ end
48
+
49
+ def issue_count
50
+ error_count + warning_count
51
+ end
52
+
53
+ def fixable_count
54
+ (@errors + @warnings).count(&:fixable?)
55
+ end
56
+
57
+ def failed_requirements
58
+ @errors.select(&:requirement_id)
59
+ end
60
+
61
+ def apply_fixes
62
+ return self unless fixable?
63
+
64
+ # Create a copy of the document for fixing
65
+ @fixed_document = @document.dup
66
+ @fixes_applied = []
67
+
68
+ # Apply fixes for errors first, then warnings
69
+ fixable_issues = (@errors + @warnings).select(&:fixable?)
70
+
71
+ fixable_issues.each do |issue|
72
+ @fixes_applied << issue if issue.apply_fix
73
+ end
74
+
75
+ self
76
+ end
77
+
78
+ def to_s(format: :text)
79
+ case format
80
+ when :text
81
+ to_text
82
+ when :json
83
+ to_json
84
+ when :hash
85
+ to_h
86
+ else
87
+ to_text
88
+ end
89
+ end
90
+
91
+ def to_h
92
+ {
93
+ file: @document.file_path,
94
+ profile: @profile.name,
95
+ valid: valid?,
96
+ errors: @errors.map(&:to_h),
97
+ warnings: @warnings.map(&:to_h),
98
+ fixes_applied: @fixes_applied.size,
99
+ fixable: fixable_count,
100
+ }
101
+ end
102
+
103
+ def to_json(*args)
104
+ JSON.pretty_generate(to_h, *args)
105
+ end
106
+
107
+ private
108
+
109
+ def to_text
110
+ lines = []
111
+ lines << "SVG Conformance Report"
112
+ lines << "=" * 22
113
+ lines << ""
114
+ lines << "File: #{@document.file_path || 'inline'}"
115
+ lines << "Profile: #{@profile.name} (#{@profile.description})"
116
+ lines << "Status: #{valid? ? 'VALID' : 'INVALID'}"
117
+ lines << ""
118
+
119
+ if has_errors?
120
+ lines << "Errors (#{error_count}):"
121
+ @errors.each_with_index do |error, i|
122
+ lines << " #{i + 1}. #{error}"
123
+ end
124
+ lines << ""
125
+ end
126
+
127
+ if has_warnings?
128
+ lines << "Warnings (#{warning_count}):"
129
+ @warnings.each_with_index do |warning, i|
130
+ lines << " #{i + 1}. #{warning}"
131
+ end
132
+ lines << ""
133
+ end
134
+
135
+ if fixable?
136
+ lines << "Fixes Available: #{fixable_count}"
137
+ lines << "Fixes Applied: #{@fixes_applied.size}" if fixed?
138
+ lines << ""
139
+ end
140
+
141
+ lines << "✓ Document is valid and conforms to the #{@profile.name} profile." if valid? && !has_warnings?
142
+
143
+ lines.join("\n")
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SvgConform
4
+ # Main validator class for SVG conformance checking
5
+ class Validator
6
+ attr_reader :options
7
+
8
+ def initialize(options = {})
9
+ @options = {
10
+ fix: false,
11
+ strict: false,
12
+ }.merge(options)
13
+ end
14
+
15
+ # Validate an SVG file
16
+ def validate_file(file_path, profile: :svg_1_2_rfc, **options)
17
+ unless File.exist?(file_path)
18
+ raise ValidationError,
19
+ "File not found: #{file_path}"
20
+ end
21
+
22
+ document = Document.from_file(file_path)
23
+ validate_document(document, profile: profile, **options)
24
+ end
25
+
26
+ # Validate SVG content string
27
+ def validate(svg_content, profile: :svg_1_2_rfc, **options)
28
+ document = Document.from_content(svg_content)
29
+ validate_document(document, profile: profile, **options)
30
+ end
31
+
32
+ # Validate a Document object
33
+ def validate_document(document, profile: :svg_1_2_rfc, **options)
34
+ merged_options = @options.merge(options)
35
+ profile_obj = resolve_profile(profile)
36
+
37
+ # Perform validation
38
+ result = profile_obj.validate(document)
39
+
40
+ # Apply fixes if requested
41
+ result.apply_fixes if merged_options[:fix] && result.fixable?
42
+
43
+ result
44
+ end
45
+
46
+ # Validate multiple files
47
+ def validate_files(file_paths, profile: :svg_1_2_rfc, **options)
48
+ results = {}
49
+
50
+ file_paths.each do |file_path|
51
+ results[file_path] =
52
+ validate_file(file_path, profile: profile, **options)
53
+ rescue StandardError => e
54
+ results[file_path] = create_error_result(file_path, e)
55
+ end
56
+
57
+ results
58
+ end
59
+
60
+ # Get available profiles
61
+ def available_profiles
62
+ SvgConform::Profiles.available_profiles
63
+ end
64
+
65
+ private
66
+
67
+ def resolve_profile(profile)
68
+ case profile
69
+ when Symbol, String
70
+ SvgConform::Profiles.get(profile) || raise(ProfileError,
71
+ "Unknown profile: #{profile}")
72
+ when Profile
73
+ profile
74
+ else
75
+ raise ProfileError, "Invalid profile type: #{profile.class}"
76
+ end
77
+ end
78
+
79
+ def create_error_result(file_path, error)
80
+ # Create a minimal error result for files that couldn't be processed
81
+ OpenStruct.new(
82
+ valid?: false,
83
+ errors: [error],
84
+ warnings: [],
85
+ file_path: file_path,
86
+ error?: true,
87
+ to_s: -> { "Error processing #{file_path}: #{error.message}" },
88
+ )
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SvgConform
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ # Configure Lutaml::Model adapters
6
+ Lutaml::Model::Config.configure do |config|
7
+ config.xml_adapter_type = :nokogiri
8
+ config.json_adapter_type = :standard_json
9
+ config.yaml_adapter_type = :standard_yaml
10
+ end
11
+
12
+ require_relative "svg_conform/version"
13
+ require_relative "svg_conform/document"
14
+ require_relative "svg_conform/validation_context"
15
+ require_relative "svg_conform/validation_result"
16
+ require_relative "svg_conform/profiles"
17
+ require_relative "svg_conform/requirements"
18
+ require_relative "svg_conform/remediations"
19
+ require_relative "svg_conform/remediation_result"
20
+ require_relative "svg_conform/external_checkers"
21
+ require_relative "svg_conform/cli"
22
+
23
+ module SvgConform
24
+ class Error < StandardError; end
25
+ class ParseError < Error; end
26
+ class ValidationError < Error; end
27
+ class ProfileError < Error; end
28
+ class RuleError < Error; end
29
+
30
+ # Autoload core classes
31
+ autoload :Validator, File.expand_path("svg_conform/validator", __dir__)
32
+ autoload :Fixer, "svg_conform/fixer"
33
+ autoload :RemediationEngine, "svg_conform/remediation_engine"
34
+ autoload :ConformanceReport,
35
+ File.expand_path("svg_conform/conformance_report", __dir__)
36
+ autoload :BatchReport,
37
+ File.expand_path("svg_conform/batch_report", __dir__)
38
+ autoload :ReportComparator,
39
+ File.expand_path("svg_conform/report_comparator", __dir__)
40
+ autoload :NamespaceRemediation, "svg_conform/namespace_remediation"
41
+ autoload :Cli, "svg_conform/cli"
42
+
43
+ # Autoload command classes
44
+ module Commands
45
+ autoload :Check, "svg_conform/commands/check"
46
+ autoload :Compare, "svg_conform/commands/compare"
47
+ autoload :Compatibility,
48
+ File.expand_path("svg_conform/commands/compatibility", __dir__)
49
+ autoload :GenerateReports,
50
+ File.expand_path("svg_conform/commands/generate_reports", __dir__)
51
+ autoload :Profiles, "svg_conform/commands/profiles"
52
+ end
53
+
54
+ # Convenience method to create a new validator
55
+ def self.validator
56
+ Validator.new
57
+ end
58
+
59
+ # Validate a file with the specified profile
60
+ def self.validate_file(file_path, profile: :svg_1_2_rfc, **options)
61
+ validator.validate_file(file_path, profile: profile, **options)
62
+ end
63
+
64
+ # Validate SVG content with the specified profile
65
+ def self.validate(svg_content, profile: :svg_1_2_rfc, **options)
66
+ validator.validate(svg_content, profile: profile, **options)
67
+ end
68
+ end