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,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ # Validates that no external font references are present
8
+ # All fonts must be embedded in the SVG
9
+ class NoExternalFontsRequirement < BaseRequirement
10
+ attribute :type, :string, default: -> { "NoExternalFontsRequirement" }
11
+ attribute :check_font_face, :boolean, default: true
12
+ attribute :check_style_fonts, :boolean, default: true
13
+
14
+ yaml do
15
+ map "id", to: :id
16
+ map "description", to: :description
17
+ map "type", to: :type
18
+ map "check_font_face", to: :check_font_face
19
+ map "check_style_fonts", to: :check_style_fonts
20
+ end
21
+
22
+ def check(node, context)
23
+ return unless element?(node)
24
+
25
+ case node.name
26
+ when "style"
27
+ check_style_element(node, context) if check_style_fonts
28
+ when "font-face"
29
+ check_font_face_element(node, context) if check_font_face
30
+ else
31
+ check_style_attribute(node, context) if check_style_fonts
32
+ end
33
+ end
34
+
35
+ def should_check_node?(node, context = nil)
36
+ return false unless element?(node)
37
+ return false if context&.node_structurally_invalid?(node)
38
+
39
+ node.name == "style" ||
40
+ node.name == "font-face" ||
41
+ has_style_attribute?(node)
42
+ end
43
+
44
+ private
45
+
46
+ def check_style_element(node, context)
47
+ # Check for @font-face with external src in style elements
48
+ content = node.text || ""
49
+
50
+ # Match @font-face blocks
51
+ content.scan(/@font-face\s*\{([^}]+)\}/m) do |match|
52
+ font_face_content = match[0]
53
+
54
+ # Check for src with url() that is not data: URI
55
+ if font_face_content =~ /src\s*:\s*url\s*\(\s*['"]?([^'")\s]+)['"]?\s*\)/i
56
+ url = ::Regexp.last_match(1)
57
+ unless embedded_font?(url)
58
+ context.add_error(
59
+ requirement_id: id,
60
+ message: "External font reference not allowed: #{url}. Fonts must be embedded as data URIs.",
61
+ node: node,
62
+ severity: :error,
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def check_font_face_element(node, context)
70
+ # Check font-face-src and font-face-uri elements
71
+ node.xpath(".//font-face-src/font-face-uri").each do |uri_node|
72
+ href = get_attribute(uri_node,
73
+ "xlink:href") || get_attribute(uri_node, "href")
74
+ if href && !embedded_font?(href)
75
+ context.add_error(
76
+ requirement_id: id,
77
+ message: "External font reference in font-face-uri not allowed: #{href}. Fonts must be embedded as data URIs.",
78
+ node: uri_node,
79
+ severity: :error,
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ def check_style_attribute(node, context)
86
+ style_value = get_attribute(node, "style")
87
+ return unless style_value
88
+
89
+ # Check for font-family with url() references
90
+ return unless style_value =~ /font-family\s*:\s*.*url\s*\(\s*['"]?([^'")\s]+)['"]?\s*\)/i
91
+
92
+ url = ::Regexp.last_match(1)
93
+ return if embedded_font?(url)
94
+
95
+ context.add_error(
96
+ requirement_id: id,
97
+ message: "External font URL in style attribute not allowed: #{url}. Fonts must be embedded as data URIs.",
98
+ node: node,
99
+ severity: :error,
100
+ )
101
+ end
102
+
103
+ def has_style_attribute?(node)
104
+ !get_attribute(node, "style").nil?
105
+ end
106
+
107
+ def embedded_font?(url)
108
+ return true if url.nil? || url.empty?
109
+
110
+ # Data URLs are embedded
111
+ return true if url.start_with?("data:")
112
+
113
+ # Fragment identifiers (internal references) are allowed
114
+ return true if url.start_with?("#")
115
+
116
+ # Everything else is external
117
+ false
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ # Validates that no external image references are present
8
+ # All images must be embedded in the SVG
9
+ class NoExternalImagesRequirement < BaseRequirement
10
+ attribute :type, :string, default: -> { "NoExternalImagesRequirement" }
11
+ attribute :check_image_elements, :boolean, default: true
12
+ attribute :check_style_images, :boolean, default: true
13
+
14
+ yaml do
15
+ map "id", to: :id
16
+ map "description", to: :description
17
+ map "type", to: :type
18
+ map "check_image_elements", to: :check_image_elements
19
+ map "check_style_images", to: :check_style_images
20
+ end
21
+
22
+ def check(node, context)
23
+ return unless element?(node)
24
+
25
+ case node.name
26
+ when "image"
27
+ check_image_element(node, context) if check_image_elements
28
+ else
29
+ check_style_attribute(node, context) if check_style_images
30
+ end
31
+ end
32
+
33
+ def should_check_node?(node, context = nil)
34
+ return false unless element?(node)
35
+ return false if context&.node_structurally_invalid?(node)
36
+
37
+ node.name == "image" || has_style_attribute?(node)
38
+ end
39
+
40
+ private
41
+
42
+ def check_image_element(node, context)
43
+ # Check href and xlink:href attributes
44
+ href = get_attribute(node, "href") || get_attribute(node, "xlink:href")
45
+ return unless href && !embedded_image?(href)
46
+
47
+ context.add_error(
48
+ requirement_id: id,
49
+ message: "External image reference not allowed: #{href}. Images must be embedded as data URIs.",
50
+ node: node,
51
+ severity: :error,
52
+ )
53
+ end
54
+
55
+ def check_style_attribute(node, context)
56
+ style_value = get_attribute(node, "style")
57
+ return unless style_value
58
+
59
+ # Check for url() references to images in background, background-image, etc.
60
+ style_value.scan(/url\s*\(\s*['"]?([^'")\s]+)['"]?\s*\)/i) do
61
+ url = ::Regexp.last_match(1)
62
+ next if embedded_image?(url)
63
+
64
+ context.add_error(
65
+ requirement_id: id,
66
+ message: "External image URL in style attribute not allowed: #{url}. Images must be embedded as data URIs.",
67
+ node: node,
68
+ severity: :error,
69
+ )
70
+ end
71
+ end
72
+
73
+ def has_style_attribute?(node)
74
+ !get_attribute(node, "style").nil?
75
+ end
76
+
77
+ def embedded_image?(url)
78
+ return true if url.nil? || url.empty?
79
+
80
+ # Data URLs are embedded
81
+ return true if url.start_with?("data:")
82
+
83
+ # Fragment identifiers (internal references) are allowed
84
+ return true if url.start_with?("#")
85
+
86
+ # Everything else is external
87
+ false
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SvgConform
4
+ module Requirements
5
+ # Detects CSS style properties that should be promoted to attributes
6
+ class StylePromotionRequirement < BaseRequirement
7
+ # CSS properties that can be promoted to SVG attributes
8
+ PROMOTABLE_PROPERTIES = %w[
9
+ fill stroke stroke-width stroke-opacity stroke-linecap stroke-linejoin
10
+ fill-opacity fill-rule text-anchor font-family font-size font-weight
11
+ font-style opacity visibility display
12
+ ].freeze
13
+
14
+ def validate_document(document, context)
15
+ document.xpath("//*[@style]").each do |element|
16
+ style_attr = element["style"]
17
+ next if style_attr.nil? || style_attr.strip.empty?
18
+
19
+ validate_style_properties(element, style_attr, context)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def validate_style_properties(element, style_attr, context)
26
+ # Parse CSS style declarations
27
+ style_declarations = parse_style_declarations(style_attr)
28
+
29
+ style_declarations.each do |property, value|
30
+ next unless PROMOTABLE_PROPERTIES.include?(property)
31
+
32
+ # Report each promotable property as an issue
33
+ context.add_error(
34
+ requirement_id: id,
35
+ message: "Style property '#{property}' can be promoted to attribute",
36
+ node: element,
37
+ severity: :info,
38
+ data: {
39
+ attribute: "style",
40
+ property: property,
41
+ value: value,
42
+ element: element.name,
43
+ suggestion: "Move '#{property}:#{value}' from style to #{property} attribute",
44
+ },
45
+ )
46
+ end
47
+ end
48
+
49
+ def parse_style_declarations(style_attr)
50
+ declarations = {}
51
+
52
+ # Split by semicolon and parse each declaration
53
+ style_attr.split(";").each do |declaration|
54
+ next if declaration.strip.empty?
55
+
56
+ parts = declaration.split(":", 2)
57
+ next unless parts.length == 2
58
+
59
+ property = parts[0].strip
60
+ value = parts[1].strip
61
+
62
+ # Remove any trailing semicolon from value
63
+ value = value.chomp(";")
64
+
65
+ declarations[property] = value
66
+ end
67
+
68
+ declarations
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ # Unified style requirement that handles all style validation:
8
+ # - Style syntax validation
9
+ # - Allowed style properties (whitelist/blacklist)
10
+ # - Property value validation
11
+ class StyleRequirement < BaseRequirement
12
+ attribute :type, :string, default: -> { "StyleRequirement" }
13
+ attribute :allowed_properties, :string, collection: true, default: -> {
14
+ []
15
+ }
16
+ attribute :disallowed_properties, :string, collection: true, default: -> {
17
+ []
18
+ }
19
+ attribute :property_values, :hash, default: -> { {} }
20
+ attribute :property_types, :hash, default: -> { {} }
21
+
22
+ yaml do
23
+ map "id", to: :id
24
+ map "description", to: :description
25
+ map "type", to: :type
26
+ map "allowed_properties", to: :allowed_properties
27
+ map "disallowed_properties", to: :disallowed_properties
28
+ map "property_values", to: :property_values
29
+ map "property_types", to: :property_types
30
+ end
31
+
32
+ def check(node, context)
33
+ return unless element?(node)
34
+
35
+ style_value = get_attribute(node, "style")
36
+ return unless style_value
37
+ return if style_value.strip.empty?
38
+
39
+ # 1. Check for malformed style syntax
40
+ check_malformed_syntax(style_value, node, context)
41
+
42
+ # 2. Check for allowed/disallowed properties and validate their values
43
+ check_style_properties(style_value, node, context)
44
+ end
45
+
46
+ private
47
+
48
+ def check_malformed_syntax(style_value, node, context)
49
+ # Check for malformed fields like ['malformed']
50
+ if /\[['"][^'"]*['"]\]/.match?(style_value)
51
+ context.add_error(
52
+ requirement_id: id,
53
+ message: "Malformed field '#{style_value.match(/\[['"][^'"]*['"]\]/)[0]}' in style attribute found. Field removed.",
54
+ node: node,
55
+ severity: :error,
56
+ data: { attribute: "style", value: style_value },
57
+ )
58
+ end
59
+
60
+ # Check for other malformed syntax patterns
61
+ # Invalid property declarations without colons
62
+ style_value.split(";").each do |declaration|
63
+ declaration = declaration.strip
64
+ next if declaration.empty?
65
+
66
+ next if declaration.include?(":")
67
+
68
+ context.add_error(
69
+ requirement_id: id,
70
+ message: "Malformed style declaration '#{declaration}' found. Declaration removed.",
71
+ node: node,
72
+ severity: :error,
73
+ data: { attribute: "style", declaration: declaration },
74
+ )
75
+ end
76
+ end
77
+
78
+ def check_style_properties(style_value, node, context)
79
+ style_value.split(";").each do |declaration|
80
+ declaration = declaration.strip
81
+ next if declaration.empty?
82
+
83
+ next unless declaration.include?(":")
84
+
85
+ property, value = declaration.split(":", 2).map(&:strip)
86
+ property = property.downcase
87
+
88
+ # Check if property is allowed
89
+ if property_invalid?(property)
90
+ context.add_error(
91
+ requirement_id: id,
92
+ message: "Style property '#{property}' removed",
93
+ node: node,
94
+ severity: :error,
95
+ data: { attribute: "style", property: property },
96
+ )
97
+ next
98
+ end
99
+
100
+ # Skip value validation for color properties - handled by ColorRestrictionsRequirement
101
+ color_properties = %w[fill stroke color stop-color flood-color
102
+ lighting-color]
103
+ next if color_properties.include?(property)
104
+
105
+ # Skip value validation for font-family - handled by FontFamilyRequirement
106
+ next if property == "font-family"
107
+
108
+ # Check if property value is valid
109
+ next unless property_value_invalid?(property, value)
110
+
111
+ context.add_error(
112
+ requirement_id: id,
113
+ message: "Invalid value '#{value}' for style property '#{property}'",
114
+ node: node,
115
+ severity: :error,
116
+ data: { attribute: "style", property: property, value: value },
117
+ )
118
+ end
119
+ end
120
+
121
+ def property_invalid?(property)
122
+ # If allowed_properties is specified (whitelist approach), use that
123
+ if allowed_properties && !allowed_properties.empty?
124
+ allowed_props = allowed_properties
125
+ allowed_props = [allowed_props] if allowed_props.is_a?(String)
126
+ return !allowed_props.include?(property)
127
+ end
128
+
129
+ # Otherwise, use disallowed_properties (blacklist approach)
130
+ if disallowed_properties && !disallowed_properties.empty?
131
+ disallowed_props = disallowed_properties
132
+ disallowed_props = [disallowed_props] if disallowed_props.is_a?(String)
133
+ return disallowed_props.include?(property)
134
+ end
135
+
136
+ # If neither is specified, allow all properties
137
+ false
138
+ end
139
+
140
+ def property_value_invalid?(property, value)
141
+ # First check explicit allowed values
142
+ if property_values && property_values[property]
143
+ allowed_values = property_values[property]
144
+ unless allowed_values.nil? || allowed_values.empty?
145
+ # Convert to array if it's a single value
146
+ allowed_values = [allowed_values] if allowed_values.is_a?(String)
147
+
148
+ # Separate literal values from type references (those starting with '<')
149
+ literal_values = allowed_values.reject { |v| v.start_with?("<") }
150
+ type_references = allowed_values.select { |v| v.start_with?("<") }
151
+
152
+ # Check literal values first (case-insensitive)
153
+ return false if literal_values.map(&:downcase).include?(value.downcase)
154
+
155
+ # If there are type references, validate against those types
156
+ if type_references.any?
157
+ type_references.each do |type_ref|
158
+ return false if validate_property_type(value, type_ref)
159
+ end
160
+ # Value didn't match any type reference
161
+ else
162
+ # No type references, and didn't match literals
163
+ end
164
+ return true
165
+ end
166
+ end
167
+
168
+ # Then check type-based validation
169
+ if property_types && property_types[property]
170
+ property_type = property_types[property]
171
+ return !validate_property_type(value, property_type)
172
+ end
173
+
174
+ # If no validation rules are specified, allow the value
175
+ false
176
+ end
177
+
178
+ # Validate a property value against its type (from word_properties.py)
179
+ def validate_property_type(value, type)
180
+ case type
181
+ when "<color>"
182
+ validate_color_value(value)
183
+ when "<paint>"
184
+ validate_paint_value(value)
185
+ when "<number>"
186
+ validate_number_value(value)
187
+ when "<integer>"
188
+ validate_integer_value(value)
189
+ else
190
+ # Unknown type, allow it
191
+ true
192
+ end
193
+ end
194
+
195
+ # Validate color values (from basic_types['<color>'] in word_properties.py)
196
+ def validate_color_value(value)
197
+ allowed_colors = ["black", "#ffffff", "#FFFFFF", "white", "#000000",
198
+ "currentColor", "inherit"]
199
+ # Case-insensitive comparison for all values
200
+ allowed_colors_lower = allowed_colors.map(&:downcase)
201
+ allowed_colors_lower.include?(value.downcase) || allowed_colors.include?(value)
202
+ end
203
+
204
+ # Validate paint values (from basic_types['<paint>'] in word_properties.py)
205
+ def validate_paint_value(value)
206
+ # <paint> includes 'none', 'currentColor', 'inherit', and <color> values
207
+ return true if %w[none currentColor inherit].include?(value.downcase)
208
+
209
+ # Also check if it's a valid color
210
+ validate_color_value(value)
211
+ end
212
+
213
+ # Validate number values (basic numeric validation)
214
+ def validate_number_value(value)
215
+ # Allow numbers with optional units, decimals, percentages
216
+ value.match?(/^[+-]?(\d+\.?\d*|\.\d+)(%|px|em|ex|pt|pc|cm|mm|in)?$/i)
217
+ end
218
+
219
+ # Validate integer values
220
+ def validate_integer_value(value)
221
+ # Allow positive/negative integers
222
+ value.match?(/^[+-]?\d+$/)
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ # Validates that SVG documents have proper viewBox attributes
8
+ class ViewboxRequiredRequirement < BaseRequirement
9
+ attribute :type, :string, default: -> { "ViewboxRequiredRequirement" }
10
+
11
+ yaml do
12
+ map "id", to: :id
13
+ map "description", to: :description
14
+ map "type", to: :type
15
+ end
16
+
17
+ def validate_document(document, context)
18
+ root = document.root
19
+ return unless root&.name == "svg"
20
+
21
+ viewbox = get_attribute(root, "viewBox")
22
+
23
+ if viewbox.nil? || viewbox.empty?
24
+ context.add_error(
25
+ requirement: self,
26
+ node: root,
27
+ message: "SVG root element must have a viewBox attribute",
28
+ data: { missing_attribute: "viewBox" },
29
+ )
30
+
31
+ # Add informational message about calculated viewBox if width/height are present
32
+ width = get_attribute(root, "width")
33
+ height = get_attribute(root, "height")
34
+
35
+ if width && height && valid_number?(width) && valid_number?(height)
36
+ calculated_viewbox = "0 0 #{width.to_f} #{height.to_f}"
37
+ context.add_error(
38
+ requirement: self,
39
+ node: root,
40
+ message: "Trying to put in the attribute with value '#{calculated_viewbox}'",
41
+ data: {
42
+ calculated_viewbox: calculated_viewbox,
43
+ source_width: width,
44
+ source_height: height,
45
+ },
46
+ )
47
+ end
48
+ return
49
+ end
50
+
51
+ # Validate viewBox format (should be "min-x min-y width height")
52
+ # Also accept comma-separated with parentheses like "(0, 0, 100, 100)" (svgcheck is lenient)
53
+ normalized_viewbox = viewbox.strip.gsub(/[(),]/, " ").squeeze(" ")
54
+ parts = normalized_viewbox.strip.split(/\s+/)
55
+ unless parts.length == 4 && parts.all? { |part| valid_number?(part) }
56
+ context.add_error(
57
+ requirement: self,
58
+ node: root,
59
+ message: "viewBox attribute must contain four numeric values (min-x min-y width height)",
60
+ data: {
61
+ viewbox_value: viewbox,
62
+ parsed_parts: parts,
63
+ },
64
+ )
65
+ return
66
+ end
67
+
68
+ # Check that width and height are positive
69
+ width = parts[2].to_f
70
+ height = parts[3].to_f
71
+
72
+ return unless width <= 0 || height <= 0
73
+
74
+ context.add_error(
75
+ requirement: self,
76
+ node: root,
77
+ message: "viewBox width and height must be positive values",
78
+ data: {
79
+ viewbox_value: viewbox,
80
+ width: width,
81
+ height: height,
82
+ },
83
+ )
84
+ end
85
+
86
+ private
87
+
88
+ def valid_number?(str)
89
+ Float(str)
90
+ true
91
+ rescue ArgumentError
92
+ false
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "requirements/base_requirement"
4
+ require_relative "requirements/allowed_elements_requirement"
5
+ require_relative "requirements/color_restrictions_requirement"
6
+ require_relative "requirements/font_family_requirement"
7
+ require_relative "requirements/invalid_id_references_requirement"
8
+ require_relative "requirements/namespace_requirement"
9
+ require_relative "requirements/no_external_css_requirement"
10
+ require_relative "requirements/no_external_fonts_requirement"
11
+ require_relative "requirements/no_external_images_requirement"
12
+ require_relative "requirements/viewbox_required_requirement"
13
+ require_relative "requirements/namespace_attributes_requirement"
14
+ require_relative "requirements/forbidden_content_requirement"
15
+ require_relative "requirements/style_requirement"
16
+ require_relative "requirements/style_promotion_requirement"
17
+ require_relative "requirements/link_validation_requirement"
18
+ require_relative "requirements/id_reference_requirement"
19
+
20
+ module SvgConform
21
+ module Requirements
22
+ # Auto-load all requirement classes
23
+ def self.all
24
+ [
25
+ AllowedElementsRequirement,
26
+ ColorRestrictionsRequirement,
27
+ FontFamilyRequirement,
28
+ InvalidIdReferencesRequirement,
29
+ NamespaceRequirement,
30
+ NoExternalCssRequirement,
31
+ NoExternalFontsRequirement,
32
+ NoExternalImagesRequirement,
33
+ ViewboxRequiredRequirement,
34
+ NamespaceAttributesRequirement,
35
+ ForbiddenContentRequirement,
36
+ StyleRequirement,
37
+ LinkValidationRequirement,
38
+ IdReferenceRequirement,
39
+ ]
40
+ end
41
+
42
+ # Find a requirement class by name
43
+ def self.find(class_name)
44
+ const_get(class_name)
45
+ rescue NameError
46
+ nil
47
+ end
48
+ end
49
+ end