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,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ # Validates font family restrictions
8
+ class FontFamilyRequirement < BaseRequirement
9
+ attribute :type, :string, default: -> { "FontFamilyRequirement" }
10
+ attribute :allowed_families, :string, collection: true, default: -> {
11
+ %w[serif sans-serif monospace]
12
+ }
13
+ attribute :svgcheck_compatibility, :boolean, default: -> { false }
14
+ attribute :enable_fallback, :boolean, default: -> { false }
15
+ attribute :default_fallback, :string, default: -> { "sans-serif" }
16
+
17
+ yaml do
18
+ map "id", to: :id
19
+ map "description", to: :description
20
+ map "type", to: :type
21
+ map "allowed_families", to: :allowed_families
22
+ map "svgcheck_compatibility", to: :svgcheck_compatibility
23
+ map "enable_fallback", to: :enable_fallback
24
+ map "default_fallback", to: :default_fallback
25
+ end
26
+
27
+ def check(node, context)
28
+ return unless element?(node)
29
+
30
+ # Check font-family attribute only (not style properties)
31
+ # Style properties are handled by StylePromotionRequirement to avoid duplication
32
+ font_family = get_attribute(node, "font-family")
33
+ return unless font_family
34
+
35
+ if svgcheck_compatibility
36
+ check_font_family_svgcheck_mode(node, context, font_family,
37
+ "font-family")
38
+ elsif !valid_font_family?(font_family)
39
+ context.add_error(
40
+ requirement_id: id,
41
+ message: "Font family '#{font_family}' is not allowed in this profile",
42
+ node: node,
43
+ severity: :error,
44
+ data: { attribute: "font-family", value: font_family },
45
+ )
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def check_font_family_svgcheck_mode(node, context, font_family_value,
52
+ attribute_name)
53
+ # Check if the font family value is valid according to svgcheck
54
+ return if valid_font_family_svgcheck?(font_family_value)
55
+
56
+ # Generate error message matching svgcheck's exact format
57
+ context.add_error(
58
+ requirement_id: id,
59
+ message: "The attribute '#{attribute_name}' does not allow the value '#{font_family_value}', replaced with '#{default_fallback}'",
60
+ node: node,
61
+ severity: :error,
62
+ data: {
63
+ attribute: attribute_name,
64
+ original_value: font_family_value,
65
+ replacement_value: default_fallback,
66
+ },
67
+ )
68
+ end
69
+
70
+ def extract_valid_fonts(font_family_value)
71
+ # Parse font family list (comma-separated)
72
+ families = font_family_value.split(",").map(&:strip)
73
+
74
+ valid_fonts = []
75
+ families.each do |family|
76
+ # Remove quotes if present
77
+ clean_family = family.gsub(/['"]/, "").strip.downcase
78
+ valid_fonts << clean_family if allowed_families.include?(clean_family)
79
+ end
80
+
81
+ valid_fonts
82
+ end
83
+
84
+ def valid_font_family_svgcheck?(font_family)
85
+ # svgcheck allows: serif, sans-serif, monospace, inherit
86
+ # Any other value is considered invalid and gets replaced with sans-serif
87
+ svgcheck_allowed = %w[serif sans-serif monospace inherit]
88
+
89
+ # Parse font family list (can be comma-separated)
90
+ families = font_family.split(",").map(&:strip)
91
+
92
+ # Check if any family in the list is valid
93
+ families.any? do |family|
94
+ # Remove quotes if present
95
+ clean_family = family.gsub(/['"]/, "").strip.downcase
96
+ svgcheck_allowed.include?(clean_family)
97
+ end
98
+ end
99
+
100
+ def valid_font_family?(font_family)
101
+ # Parse font family list (can be comma-separated)
102
+ families = font_family.split(",").map(&:strip)
103
+
104
+ # All families must be allowed (matching svgcheck behavior)
105
+ families.all? do |family|
106
+ # Remove quotes if present
107
+ clean_family = family.gsub(/['"]/, "").strip.downcase
108
+ allowed_families.include?(clean_family)
109
+ end
110
+ end
111
+
112
+ def parse_style(style_string)
113
+ return {} if style_string.nil? || style_string.empty?
114
+
115
+ properties = {}
116
+ declarations = style_string.split(";").map(&:strip)
117
+
118
+ declarations.each do |declaration|
119
+ next if declaration.empty?
120
+
121
+ parts = declaration.split(":", 2)
122
+ next unless parts.length == 2
123
+
124
+ property = parts[0].strip
125
+ value = parts[1].strip
126
+ properties[property] = value
127
+ end
128
+
129
+ properties
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ class ForbiddenContentRequirement < BaseRequirement
8
+ attribute :forbidden_elements, :string, collection: true, default: -> {
9
+ []
10
+ }
11
+ attribute :forbidden_attributes, :string, collection: true, default: -> {
12
+ []
13
+ }
14
+
15
+ yaml do
16
+ map "id", to: :id
17
+ map "description", to: :description
18
+ map "type", to: :type
19
+ map "forbidden_elements", to: :forbidden_elements
20
+ map "forbidden_attributes", to: :forbidden_attributes
21
+ end
22
+
23
+ def validate_document(document, context)
24
+ document.traverse do |node|
25
+ check(node, context) if should_check_node?(node)
26
+ end
27
+ end
28
+
29
+ def check(node, context)
30
+ return unless element?(node)
31
+
32
+ # Check if this is a forbidden element
33
+ if forbidden_elements.include?(node.name)
34
+ context.add_error(
35
+ requirement_id: id,
36
+ message: "Forbidden element '#{node.name}' is not allowed",
37
+ node: node,
38
+ severity: :error,
39
+ )
40
+ end
41
+
42
+ # Check for forbidden attributes
43
+ if node.respond_to?(:attributes) && node.attributes
44
+ node.attributes.each do |attr|
45
+ attr_name = attr.respond_to?(:name) ? attr.name : attr.to_s
46
+
47
+ if forbidden_attributes.include?(attr_name)
48
+ context.add_error(
49
+ requirement_id: id,
50
+ message: "Forbidden attribute '#{attr_name}' is not allowed",
51
+ node: node,
52
+ severity: :error,
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+ require "set"
5
+
6
+ module SvgConform
7
+ module Requirements
8
+ class IdReferenceRequirement < BaseRequirement
9
+ def validate_document(document, context)
10
+ # Collect all IDs in the document
11
+ ids = Set.new
12
+ document.xpath("//*[@id]").each do |element|
13
+ id_value = element["id"]
14
+ ids.add(id_value) if id_value && !id_value.empty?
15
+ end
16
+
17
+ # Check references to IDs
18
+ check_url_references(document, ids, context)
19
+ check_href_references(document, ids, context)
20
+ check_other_references(document, ids, context)
21
+ end
22
+
23
+ private
24
+
25
+ def check_url_references(document, ids, context)
26
+ # Check url() references in style attributes and CSS properties
27
+ url_attributes = %w[fill stroke marker-start marker-mid marker-end
28
+ clip-path mask filter]
29
+
30
+ url_attributes.each do |attr_name|
31
+ document.xpath("//*[@#{attr_name}]").each do |element|
32
+ attr_value = element[attr_name]
33
+ next unless attr_value
34
+
35
+ url_refs = extract_url_references(attr_value)
36
+ url_refs.each do |ref_id|
37
+ next if ids.include?(ref_id)
38
+
39
+ context.add_error(
40
+ node: element,
41
+ message: "Reference to undefined ID '#{ref_id}' in attribute '#{attr_name}'",
42
+ requirement_id: id,
43
+ )
44
+ end
45
+ end
46
+ end
47
+
48
+ # Check style attributes
49
+ document.xpath("//*[@style]").each do |element|
50
+ style_attr = element["style"]
51
+ next unless style_attr
52
+
53
+ url_refs = extract_url_references(style_attr)
54
+ url_refs.each do |ref_id|
55
+ next if ids.include?(ref_id)
56
+
57
+ context.add_error(
58
+ node: element,
59
+ message: "Reference to undefined ID '#{ref_id}' in style attribute",
60
+ requirement_id: id,
61
+ )
62
+ end
63
+ end
64
+ end
65
+
66
+ def check_href_references(document, ids, context)
67
+ # Check href and xlink:href references
68
+ document.xpath("//*[@href or @*[local-name()='href']]").each do |element|
69
+ href_value = element["href"]
70
+
71
+ # Check for xlink:href if regular href is not present
72
+ if href_value.nil?
73
+ href_value = element.attributes.find do |attr|
74
+ attr.name == "href" && attr.namespace&.uri == "http://www.w3.org/1999/xlink"
75
+ end&.value
76
+ end
77
+
78
+ next unless href_value
79
+
80
+ # Only check fragment identifiers (starting with #)
81
+ next unless href_value.start_with?("#")
82
+
83
+ ref_id = href_value[1..] # Remove the #
84
+ next if ids.include?(ref_id)
85
+
86
+ context.add_error(
87
+ node: element,
88
+ message: "Reference to undefined ID '#{ref_id}' in href attribute",
89
+ requirement_id: id,
90
+ )
91
+ end
92
+ end
93
+
94
+ def check_other_references(document, ids, context)
95
+ # Check other attributes that reference IDs
96
+ id_ref_attributes = %w[for aria-labelledby aria-describedby
97
+ aria-controls aria-owns]
98
+
99
+ id_ref_attributes.each do |attr_name|
100
+ document.xpath("//*[@#{attr_name}]").each do |element|
101
+ attr_value = element[attr_name]
102
+ next unless attr_value
103
+
104
+ # These attributes can contain space-separated lists of IDs
105
+ ref_ids = attr_value.split(/\s+/)
106
+ ref_ids.each do |ref_id|
107
+ next if ref_id.empty?
108
+
109
+ next if ids.include?(ref_id)
110
+
111
+ context.add_error(
112
+ node: element,
113
+ message: "Reference to undefined ID '#{ref_id}' in #{attr_name} attribute",
114
+ requirement_id: id,
115
+ )
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def extract_url_references(value)
122
+ refs = []
123
+
124
+ # Match url(#id) patterns
125
+ value.scan(/url\(#([^)]+)\)/) do |match|
126
+ refs << match[0]
127
+ end
128
+
129
+ refs
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ # Requirement to validate that ID references point to existing elements
8
+ # Based on the Lucid SVG fix script that removes use elements with invalid IDREF
9
+ class InvalidIdReferencesRequirement < BaseRequirement
10
+ attribute :type, :string, default: -> { "InvalidIdReferencesRequirement" }
11
+ attribute :check_use_elements, :boolean, default: true
12
+ attribute :check_other_references, :boolean, default: false
13
+ attribute :strict_mode, :boolean, default: false
14
+
15
+ yaml do
16
+ map "id", to: :id
17
+ map "description", to: :description
18
+ map "type", to: :type
19
+ map "check_use_elements", to: :check_use_elements
20
+ map "check_other_references", to: :check_other_references
21
+ map "strict_mode", to: :strict_mode
22
+ end
23
+
24
+ def validate_document(document, context)
25
+ # Collect all existing IDs in the document
26
+ existing_ids = collect_existing_ids(document)
27
+ context.set_data(:existing_ids, existing_ids)
28
+
29
+ # Check for invalid references
30
+ super(document, context)
31
+ end
32
+
33
+ def check(node, context)
34
+ return unless element?(node)
35
+
36
+ if check_use_elements && node.name == "use"
37
+ check_use_element(node,
38
+ context)
39
+ end
40
+
41
+ return unless check_other_references
42
+
43
+ check_other_id_references(node, context)
44
+ end
45
+
46
+ private
47
+
48
+ def collect_existing_ids(document)
49
+ ids = Set.new
50
+ document.traverse do |node|
51
+ next unless element?(node)
52
+
53
+ id_attr = get_attribute(node, "id")
54
+ ids.add(id_attr) if id_attr && !id_attr.empty?
55
+ end
56
+ ids
57
+ end
58
+
59
+ def check_use_element(node, context)
60
+ href = get_attribute(node, "xlink:href") || get_attribute(node, "href")
61
+ return unless href&.start_with?("#")
62
+
63
+ id_ref = href[1..] # Remove # prefix
64
+ return if id_ref.empty?
65
+
66
+ existing_ids = context.get_data(:existing_ids)
67
+ return if existing_ids.include?(id_ref)
68
+
69
+ context.add_error(
70
+ requirement: self,
71
+ node: node,
72
+ message: "use element references non-existent ID: #{id_ref}",
73
+ data: { invalid_id: id_ref, href: href },
74
+ )
75
+ end
76
+
77
+ def check_other_id_references(node, context)
78
+ # Check other attributes that reference IDs
79
+ id_reference_attributes = %w[
80
+ clip-path mask filter marker-start marker-mid marker-end
81
+ fill stroke
82
+ ]
83
+
84
+ existing_ids = context.get_data(:existing_ids)
85
+
86
+ id_reference_attributes.each do |attr_name|
87
+ attr_value = get_attribute(node, attr_name)
88
+ next unless attr_value
89
+
90
+ # Extract ID from url(#id) format
91
+ next unless attr_value =~ /^url\(#(.+)\)$/
92
+
93
+ id_ref = Regexp.last_match(1)
94
+ next if existing_ids.include?(id_ref)
95
+
96
+ context.add_error(
97
+ requirement: self,
98
+ node: node,
99
+ message: "#{attr_name} references non-existent ID: #{id_ref}",
100
+ data: { invalid_id: id_ref, attribute: attr_name,
101
+ value: attr_value },
102
+ )
103
+ end
104
+
105
+ # Check style attribute for ID references
106
+ check_style_id_references(node, context, existing_ids)
107
+ end
108
+
109
+ def check_style_id_references(node, context, existing_ids)
110
+ style_value = get_attribute(node, "style")
111
+ return unless style_value
112
+
113
+ styles = parse_style(style_value)
114
+
115
+ styles.each do |property, value|
116
+ next unless value =~ /^url\(#(.+)\)$/
117
+
118
+ id_ref = Regexp.last_match(1)
119
+ next if existing_ids.include?(id_ref)
120
+
121
+ context.add_error(
122
+ requirement: self,
123
+ node: node,
124
+ message: "style property #{property} references non-existent ID: #{id_ref}",
125
+ data: { invalid_id: id_ref, style_property: property,
126
+ value: value },
127
+ )
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_requirement"
4
+
5
+ module SvgConform
6
+ module Requirements
7
+ class LinkValidationRequirement < BaseRequirement
8
+ def check(node, context)
9
+ return unless element?(node)
10
+
11
+ # Check href attributes (both href and xlink:href)
12
+ href_value = get_attribute(node, "href")
13
+
14
+ # Check for xlink:href if regular href is not present
15
+ if href_value.nil? && node.respond_to?(:attributes)
16
+ xlink_href = node.attributes.find do |attr|
17
+ attr.name == "href" && attr.namespace&.uri == "http://www.w3.org/1999/xlink"
18
+ end
19
+ href_value = xlink_href&.value
20
+ end
21
+
22
+ if href_value && !ascii_only?(href_value)
23
+ context.add_error(
24
+ requirement_id: id,
25
+ message: "Link href '#{href_value}' contains non-ASCII characters",
26
+ node: node,
27
+ severity: :error,
28
+ )
29
+ end
30
+
31
+ # Check other IRI attributes
32
+ iri_attributes = %w[src action formaction cite longdesc usemap]
33
+ iri_attributes.each do |attr_name|
34
+ iri_value = get_attribute(node, attr_name)
35
+ next unless iri_value
36
+
37
+ next if ascii_only?(iri_value)
38
+
39
+ context.add_error(
40
+ requirement_id: id,
41
+ message: "IRI attribute '#{attr_name}' value '#{iri_value}' contains non-ASCII characters",
42
+ node: node,
43
+ severity: :error,
44
+ )
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def ascii_only?(string)
51
+ string.ascii_only?
52
+ end
53
+ end
54
+ end
55
+ end