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,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser"
4
+ require_relative "../../conformance_report"
5
+
6
+ module SvgConform
7
+ module ExternalCheckers
8
+ module Svgcheck
9
+ # Generates ConformanceReport objects from svgcheck outputs
10
+ class ReportGenerator
11
+ def initialize(parser: nil)
12
+ @parser = parser || Parser.new
13
+ end
14
+
15
+ # Generate a report from svgcheck output files
16
+ def generate_from_files(filename, output_file: nil, error_file: nil,
17
+ code_file: nil)
18
+ output_content = output_file && File.exist?(output_file) ? File.read(output_file) : nil
19
+ error_content = error_file && File.exist?(error_file) ? File.read(error_file) : nil
20
+ exit_code = code_file && File.exist?(code_file) ? File.read(code_file).strip.to_i : 0
21
+
22
+ generate_from_content(filename, output_content, error_content,
23
+ exit_code)
24
+ end
25
+
26
+ # Generate a report from svgcheck output content
27
+ def generate_from_content(filename, output_content,
28
+ error_content = nil, exit_code = 0)
29
+ # svgcheck writes validation messages to stderr, not stdout
30
+ # So we use error_content as the main content to parse
31
+ main_content = error_content || output_content
32
+
33
+ report = @parser.parse(main_content, nil, filename: filename)
34
+
35
+ # Set additional metadata
36
+ report.profile = "svg_1_2_rfc" # svgcheck is specifically for SVG 1.2 RFC
37
+ report.version = detect_svgcheck_version
38
+
39
+ # Use exit code to determine validity if needed
40
+ if exit_code != 0 && report.valid
41
+ report.valid = false
42
+ end
43
+
44
+ report
45
+ end
46
+
47
+ # Generate reports for both check and repair modes
48
+ def generate_comparative_reports(filename, check_outputs: nil,
49
+ repair_outputs: nil)
50
+ reports = {}
51
+
52
+ if check_outputs
53
+ reports[:check] = generate_from_content(
54
+ filename,
55
+ check_outputs[:stdout],
56
+ check_outputs[:stderr],
57
+ check_outputs[:exit_code],
58
+ )
59
+ end
60
+
61
+ if repair_outputs
62
+ reports[:repair] = generate_from_content(
63
+ filename,
64
+ repair_outputs[:stdout],
65
+ repair_outputs[:stderr],
66
+ repair_outputs[:exit_code],
67
+ )
68
+ end
69
+
70
+ reports
71
+ end
72
+
73
+ private
74
+
75
+ def detect_svgcheck_version
76
+ # Try to detect svgcheck version - placeholder for now
77
+ "unknown"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "output_generator"
4
+ require_relative "report_generator"
5
+ require_relative "report_comparator"
6
+ require_relative "../../validator"
7
+ require_relative "../../profiles"
8
+
9
+ module SvgConform
10
+ module ExternalCheckers
11
+ module Svgcheck
12
+ # Pipeline to ensure svg_conform outputs are compatible with svgcheck
13
+ class ValidationPipeline
14
+ attr_reader :test_files_dir, :output_dir
15
+
16
+ def initialize(test_files_dir: "svgcheck/svgcheck/Tests",
17
+ output_dir: "spec/fixtures/svgcheck")
18
+ @test_files_dir = test_files_dir
19
+ @output_dir = output_dir
20
+ @generator = OutputGenerator.new
21
+ @report_generator = ReportGenerator.new
22
+ @comparator = ReportComparator.new(test_files_dir: test_files_dir,
23
+ svgcheck_outputs_dir: output_dir)
24
+ end
25
+
26
+ # Run complete validation pipeline
27
+ def run_validation_pipeline(mode: :both)
28
+ results = {
29
+ stage: "complete_pipeline",
30
+ total_files: 0,
31
+ successful_generations: 0,
32
+ successful_comparisons: 0,
33
+ failed_generations: 0,
34
+ failed_comparisons: 0,
35
+ compatibility_issues: [],
36
+ summary: {},
37
+ }
38
+
39
+ test_files = find_test_files
40
+ results[:total_files] = test_files.length
41
+
42
+ puts "🚀 Starting validation pipeline for #{test_files.length} files..."
43
+
44
+ # Stage 1: Generate svgcheck outputs
45
+ puts "\n📋 Stage 1: Generating svgcheck outputs..."
46
+ generation_results = generate_outputs_for_files(test_files, mode)
47
+ results[:successful_generations] = generation_results[:successful]
48
+ results[:failed_generations] = generation_results[:failed]
49
+
50
+ # Stage 2: Compare reports
51
+ puts "\n📋 Stage 2: Comparing svg_conform vs svgcheck reports..."
52
+ comparison_results = compare_all_reports
53
+ results[:successful_comparisons] = comparison_results[:matching]
54
+ results[:failed_comparisons] = comparison_results[:different]
55
+ results[:compatibility_issues] = comparison_results[:differences]
56
+
57
+ # Stage 3: Generate summary
58
+ results[:summary] = generate_pipeline_summary(results)
59
+
60
+ results
61
+ end
62
+
63
+ # Validate output compatibility for a single file
64
+ def validate_file_compatibility(filename)
65
+ result = {
66
+ filename: filename,
67
+ svg_conform_validation: nil,
68
+ svgcheck_outputs: nil,
69
+ comparison: nil,
70
+ compatible: false,
71
+ issues: [],
72
+ }
73
+
74
+ begin
75
+ # Generate svg_conform validation
76
+ result[:svg_conform_validation] =
77
+ generate_svg_conform_validation(filename)
78
+
79
+ # Generate svgcheck outputs
80
+ result[:svgcheck_outputs] =
81
+ @generator.generate(filename, mode: :both)
82
+
83
+ # Compare outputs
84
+ if result[:svgcheck_outputs].values.all? { |r| r[:success] }
85
+ result[:comparison] = @comparator.compare_file(filename)
86
+ result[:compatible] = result[:comparison][:identical]
87
+
88
+ unless result[:compatible]
89
+ result[:issues] =
90
+ extract_compatibility_issues(result[:comparison])
91
+ end
92
+ else
93
+ result[:issues] << "Failed to generate svgcheck outputs"
94
+ end
95
+ rescue StandardError => e
96
+ result[:issues] << "Error during validation: #{e.message}"
97
+ end
98
+
99
+ result
100
+ end
101
+
102
+ # Generate comprehensive compatibility report
103
+ def generate_compatibility_report
104
+ pipeline_results = run_validation_pipeline
105
+
106
+ {
107
+ timestamp: Time.now.iso8601,
108
+ pipeline_version: SvgConform::VERSION,
109
+ total_files: pipeline_results[:total_files],
110
+ compatibility_score: calculate_compatibility_score(pipeline_results),
111
+ results: pipeline_results,
112
+ recommendations: generate_recommendations(pipeline_results),
113
+ }
114
+ end
115
+
116
+ private
117
+
118
+ def find_test_files
119
+ return [] unless Dir.exist?(@test_files_dir)
120
+
121
+ pattern = File.join(@test_files_dir, "*.{svg,xml}")
122
+ Dir.glob(pattern).map { |path| File.basename(path) }.sort
123
+ end
124
+
125
+ def generate_outputs_for_files(test_files, mode)
126
+ results = { successful: 0, failed: 0, errors: [] }
127
+
128
+ test_files.each do |filename|
129
+ outputs = @generator.generate(filename, mode: mode)
130
+
131
+ if outputs.values.all? { |result| result[:success] }
132
+ results[:successful] += 1
133
+ puts " ✅ #{filename}"
134
+ else
135
+ results[:failed] += 1
136
+ puts " ❌ #{filename}"
137
+ results[:errors] << { filename: filename,
138
+ error: "Generation failed" }
139
+ end
140
+ rescue StandardError => e
141
+ results[:failed] += 1
142
+ puts " ❌ #{filename}: #{e.message}"
143
+ results[:errors] << { filename: filename, error: e.message }
144
+ end
145
+
146
+ results
147
+ end
148
+
149
+ def compare_all_reports
150
+ @comparator.compare_all_test_files
151
+ end
152
+
153
+ def generate_svg_conform_validation(filename)
154
+ input_file = File.join(@test_files_dir, filename)
155
+ return nil unless File.exist?(input_file)
156
+
157
+ profile = SvgConform::Profiles.load_profile(:svg_1_2_rfc)
158
+ validator = SvgConform::Validator.new(profile)
159
+ validator.validate_file(input_file)
160
+ end
161
+
162
+ def extract_compatibility_issues(comparison)
163
+ issues = []
164
+
165
+ # Add basic comparison issues
166
+ comparison[:differences]&.each do |diff|
167
+ issues << "Report difference: #{diff}"
168
+ end
169
+
170
+ # Add semantic comparison issues
171
+ if comparison[:semantic] && comparison[:semantic][:issues].any?
172
+ comparison[:semantic][:issues].each do |semantic_issue|
173
+ issues << "Semantic mismatch: #{semantic_issue[:semantic_key]} " \
174
+ "(svg_conform: #{semantic_issue[:svg_conform_count]}, " \
175
+ "svgcheck: #{semantic_issue[:svgcheck_count]})"
176
+ end
177
+ end
178
+
179
+ issues
180
+ end
181
+
182
+ def calculate_compatibility_score(pipeline_results)
183
+ return 0.0 if pipeline_results[:total_files].zero?
184
+
185
+ successful_files = pipeline_results[:successful_comparisons]
186
+ total_files = pipeline_results[:total_files]
187
+
188
+ (successful_files.to_f / total_files * 100).round(2)
189
+ end
190
+
191
+ def generate_recommendations(pipeline_results)
192
+ recommendations = []
193
+
194
+ # Generation issues
195
+ if pipeline_results[:failed_generations].positive?
196
+ recommendations << {
197
+ category: "generation",
198
+ priority: "high",
199
+ message: "#{pipeline_results[:failed_generations]} files failed svgcheck output generation. " \
200
+ "Check svgcheck installation and test file accessibility.",
201
+ }
202
+ end
203
+
204
+ # Compatibility issues
205
+ if pipeline_results[:failed_comparisons].positive?
206
+ recommendations << {
207
+ category: "compatibility",
208
+ priority: "high",
209
+ message: "#{pipeline_results[:failed_comparisons]} files show compatibility issues. " \
210
+ "Review profile configuration and requirement mappings.",
211
+ }
212
+ end
213
+
214
+ # Profile issues
215
+ compatibility_score = calculate_compatibility_score(pipeline_results)
216
+ if compatibility_score < 90.0
217
+ recommendations << {
218
+ category: "profile",
219
+ priority: "medium",
220
+ message: "Compatibility score is #{compatibility_score}%. " \
221
+ "Consider reviewing svg_1_2_rfc profile configuration against svgcheck source.",
222
+ }
223
+ end
224
+
225
+ recommendations
226
+ end
227
+
228
+ def generate_pipeline_summary(results)
229
+ {
230
+ compatibility_score: calculate_compatibility_score(results),
231
+ generation_success_rate: calculate_success_rate(
232
+ results[:successful_generations], results[:total_files]
233
+ ),
234
+ comparison_success_rate: calculate_success_rate(
235
+ results[:successful_comparisons], results[:total_files]
236
+ ),
237
+ total_issues: results[:compatibility_issues].length,
238
+ }
239
+ end
240
+
241
+ def calculate_success_rate(successful, total)
242
+ return 0.0 if total.zero?
243
+
244
+ (successful.to_f / total * 100).round(2)
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "svgcheck/parser"
4
+ require_relative "svgcheck/compatibility_engine"
5
+ require_relative "svgcheck/report_generator"
6
+ require_relative "svgcheck/output_generator"
7
+ require_relative "svgcheck/validation_pipeline"
8
+ require_relative "svgcheck/report_comparator"
9
+
10
+ module SvgConform
11
+ module ExternalCheckers
12
+ # SVGCheck external checker integration
13
+ module Svgcheck
14
+ # Main svgcheck checker class
15
+ class Checker < BaseChecker
16
+ SVGCHECK_EXECUTABLE = "python3"
17
+ SVGCHECK_MODULE_PATH = "svgcheck/svgcheck"
18
+
19
+ def initialize
20
+ super(name: "svgcheck", version: detect_version)
21
+ end
22
+
23
+ def available?
24
+ command_available?(SVGCHECK_EXECUTABLE) &&
25
+ Dir.exist?(SVGCHECK_MODULE_PATH)
26
+ end
27
+
28
+ def generate_outputs(input_file, mode: :both)
29
+ generator = OutputGenerator.new(
30
+ svgcheck_exec: SVGCHECK_EXECUTABLE,
31
+ svgcheck_path: SVGCHECK_MODULE_PATH,
32
+ )
33
+ generator.generate(input_file, mode: mode)
34
+ end
35
+
36
+ def parse_output(output_content, error_content = nil)
37
+ parser = Parser.new
38
+ parser.parse(output_content, error_content)
39
+ end
40
+
41
+ private
42
+
43
+ def detect_version
44
+ # Try to detect svgcheck version - placeholder for now
45
+ "unknown"
46
+ end
47
+
48
+ def command_available?(command)
49
+ system("which #{command} > /dev/null 2>&1") ||
50
+ system("where #{command} > NUL 2>&1") || # Windows
51
+ File.executable?(command) # Direct path
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SvgConform
4
+ # Namespace for external SVG conformance checkers
5
+ # This allows for multiple external checker integrations while keeping them separate
6
+ module ExternalCheckers
7
+ autoload :Svgcheck, "svg_conform/external_checkers/svgcheck"
8
+
9
+ # Base class for external checker integrations
10
+ class BaseChecker
11
+ attr_reader :name, :version
12
+
13
+ def initialize(name:, version: nil)
14
+ @name = name
15
+ @version = version
16
+ end
17
+
18
+ # Generate outputs for a given file - to be implemented by subclasses
19
+ def generate_outputs(input_file, mode: :both)
20
+ raise NotImplementedError, "Subclasses must implement generate_outputs"
21
+ end
22
+
23
+ # Parse checker output into a ConformanceReport - to be implemented by subclasses
24
+ def parse_output(output_content, error_content = nil)
25
+ raise NotImplementedError, "Subclasses must implement parse_output"
26
+ end
27
+
28
+ # Check if the checker is available on the system
29
+ def available?
30
+ raise NotImplementedError, "Subclasses must implement available?"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SvgConform
4
+ # Utility class for applying fixes to SVG documents
5
+ class Fixer
6
+ attr_reader :document, :fixes_applied
7
+
8
+ def initialize(document)
9
+ @document = document
10
+ @fixes_applied = []
11
+ end
12
+
13
+ # Apply a single fix
14
+ def apply_fix(fix)
15
+ return false unless fix.respond_to?(:call) || fix.respond_to?(:apply)
16
+
17
+ begin
18
+ result = if fix.respond_to?(:call)
19
+ fix.call
20
+ else
21
+ fix.apply
22
+ end
23
+
24
+ @fixes_applied << fix if result
25
+ result
26
+ rescue StandardError
27
+ false
28
+ end
29
+ end
30
+
31
+ # Apply multiple fixes
32
+ def apply_fixes(fixes)
33
+ success_count = 0
34
+
35
+ fixes.each do |fix|
36
+ success_count += 1 if apply_fix(fix)
37
+ end
38
+
39
+ success_count
40
+ end
41
+
42
+ # Apply all fixable issues from a validation result
43
+ def apply_validation_fixes(validation_result)
44
+ fixable_issues = (validation_result.errors + validation_result.warnings).select(&:fixable?)
45
+ apply_fixes(fixable_issues.map(&:fix))
46
+ end
47
+
48
+ def fixes_count
49
+ @fixes_applied.size
50
+ end
51
+
52
+ def to_xml
53
+ @document.to_xml
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "requirements"
5
+ require_relative "remediations"
6
+
7
+ module SvgConform
8
+ # Base class for SVG validation profiles using lutaml-model serialization
9
+ class Profile < Lutaml::Model::Serializable
10
+ attribute :name, :string
11
+ attribute :description, :string
12
+ attribute :import, :string
13
+ attribute :requirements, SvgConform::Requirements::BaseRequirement,
14
+ collection: true, polymorphic: true
15
+ attribute :remediations, SvgConform::Remediations::BaseRemediation,
16
+ collection: true, polymorphic: true
17
+
18
+ def self.build_class_map(dir_name, namespace, excluded_file)
19
+ target_dir = File.expand_path(dir_name, __dir__)
20
+ class_map = {}
21
+
22
+ Dir.glob(File.join(target_dir,
23
+ "*_#{dir_name.chomp('s')}.rb")).each do |file|
24
+ filename = File.basename(file, ".rb")
25
+ next if filename == excluded_file
26
+
27
+ class_name = filename.split("_").map(&:capitalize).join
28
+ class_map[class_name] = "SvgConform::#{namespace}::#{class_name}"
29
+ end
30
+
31
+ class_map
32
+ end
33
+
34
+ # Build class maps dynamically from filesystem
35
+ REQUIREMENTS_CLASS_MAP = build_class_map(
36
+ "requirements", "Requirements", "base_requirement"
37
+ )
38
+ REMEDIATIONS_CLASS_MAP = build_class_map(
39
+ "remediations", "Remediations", "base_remediation"
40
+ )
41
+
42
+ yaml do
43
+ map "name", to: :name
44
+ map "description", to: :description
45
+ map "import", to: :import
46
+ map "requirements", to: :requirements, polymorphic: {
47
+ attribute: "type",
48
+ class_map: REQUIREMENTS_CLASS_MAP,
49
+ }
50
+ map "remediations", to: :remediations, polymorphic: {
51
+ attribute: "type",
52
+ class_map: REMEDIATIONS_CLASS_MAP,
53
+ }
54
+ end
55
+
56
+ # New requirement methods
57
+ def add_requirement(requirement)
58
+ self.requirements ||= []
59
+ requirements << requirement
60
+ self
61
+ end
62
+
63
+ def remove_requirement(requirement_id)
64
+ return self unless requirements
65
+
66
+ requirements.reject! { |req| req.id == requirement_id }
67
+ self
68
+ end
69
+
70
+ def has_requirement?(requirement_id)
71
+ return false unless requirements
72
+
73
+ requirements.any? { |req| req.id == requirement_id }
74
+ end
75
+
76
+ def get_requirement(requirement_id)
77
+ return nil unless requirements
78
+
79
+ requirements.find { |req| req.id == requirement_id }
80
+ end
81
+
82
+ def requirement_count
83
+ requirements&.size || 0
84
+ end
85
+
86
+ # New remediation methods
87
+ def add_remediation(remediation)
88
+ self.remediations ||= []
89
+ remediations << remediation
90
+ self
91
+ end
92
+
93
+ def remove_remediation(remediation_id)
94
+ return self unless remediations
95
+
96
+ remediations.reject! { |rem| rem.id == remediation_id }
97
+ self
98
+ end
99
+
100
+ def has_remediation?(remediation_id)
101
+ return false unless remediations
102
+
103
+ remediations.any? { |rem| rem.id == remediation_id }
104
+ end
105
+
106
+ def get_remediation(remediation_id)
107
+ return nil unless remediations
108
+
109
+ remediations.find { |rem| rem.id == remediation_id }
110
+ end
111
+
112
+ def remediation_count
113
+ remediations&.size || 0
114
+ end
115
+
116
+ def validate(document)
117
+ context = ValidationContext.new(document, self)
118
+
119
+ # Validate using requirements system
120
+ requirements&.each do |requirement|
121
+ requirement.validate_document(document, context)
122
+ end
123
+
124
+ ValidationResult.new(document, self, context)
125
+ end
126
+
127
+ def validate_file(file_path)
128
+ document = Document.from_file(file_path)
129
+ validate(document)
130
+ end
131
+
132
+ # Apply remediations to a document after validation
133
+ def apply_remediations(document)
134
+ # Run validation to get failed requirements
135
+ validation_result = validate(document)
136
+
137
+ all_changes = []
138
+
139
+ # Apply remediations for failed requirements
140
+ remediations&.each do |remediation|
141
+ if remediation.should_execute?(validation_result.failed_requirements)
142
+ changes = remediation.apply(document, nil)
143
+ all_changes.concat(changes) if changes
144
+ end
145
+ end
146
+
147
+ all_changes
148
+ end
149
+
150
+ # Simple YAML loading methods for individual profiles
151
+ def self.load_from_file(file_path)
152
+ yaml_content = File.read(file_path)
153
+ from_yaml(yaml_content)
154
+ end
155
+
156
+ def self.save_to_file(profile, file_path)
157
+ File.write(file_path, profile.to_yaml)
158
+ end
159
+
160
+ def to_s
161
+ "#{name}: #{description} (#{requirement_count} checks, #{remediation_count} remediations)"
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "profile"
4
+
5
+ module SvgConform
6
+ module Profiles
7
+ PROFILES_DIR = File.expand_path("../../config/profiles", __dir__)
8
+ @@cache = {}
9
+
10
+ def self.get(profile_id)
11
+ case profile_id
12
+ when Symbol, String
13
+ load_profile(profile_id.to_s)
14
+ when Profile
15
+ profile_id
16
+ else
17
+ raise ProfileError, "Invalid profile: #{profile_id}"
18
+ end
19
+ end
20
+
21
+ def self.available_profiles
22
+ return @@cache.keys.map(&:to_sym) if @@cache.any?
23
+
24
+ profile_files = Dir.glob(File.join(PROFILES_DIR, "*.yml"))
25
+ profile_files.map { |file| File.basename(file, ".yml").to_sym }
26
+ end
27
+
28
+ def self.load(profile_name)
29
+ load_profile(profile_name.to_s)
30
+ end
31
+
32
+ def self.list
33
+ available_profiles.map(&:to_s)
34
+ end
35
+
36
+ def self.clear_cache!
37
+ @@cache.clear
38
+ end
39
+
40
+ def self.load_profile(profile_name)
41
+ return @@cache[profile_name] if @@cache[profile_name]
42
+
43
+ profile_file = File.join(PROFILES_DIR, "#{profile_name}.yml")
44
+
45
+ unless File.exist?(profile_file)
46
+ raise ProfileError,
47
+ "Profile not found: #{profile_name} (expected: #{profile_file})"
48
+ end
49
+
50
+ begin
51
+ profile = Profile.load_from_file(profile_file)
52
+ @@cache[profile_name] = profile
53
+ profile
54
+ # rescue => e
55
+ # raise e
56
+ # raise ProfileError, "Failed to load profile #{profile_name}: #{e.message}"
57
+ end
58
+ end
59
+ end
60
+ end