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,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_remediation"
4
+
5
+ module SvgConform
6
+ module Remediations
7
+ # Remediation action to remove external CSS references
8
+ class NoExternalCssRemediation < BaseRemediation
9
+ attribute :type, :string, default: -> { "NoExternalCssRemediation" }
10
+ attribute :strategy, :string, default: "remove_external_references"
11
+ attribute :preserve_inline_styles, :boolean, default: true
12
+ attribute :log_removed_elements, :boolean, default: true
13
+
14
+ def apply(document, context)
15
+ changes = []
16
+
17
+ case @strategy
18
+ when "remove_external_references"
19
+ changes += remove_external_link_elements(document, context)
20
+ changes += remove_external_imports_from_style_elements(document,
21
+ context)
22
+ changes += remove_external_urls_from_style_attributes(document,
23
+ context)
24
+ else
25
+ context.add_error(
26
+ message: "Unknown remediation strategy: #{@strategy}",
27
+ node: document.root,
28
+ )
29
+ return []
30
+ end
31
+
32
+ if @log_removed_elements && changes.any?
33
+ log_remediation(context,
34
+ "Removed #{changes.length} external CSS references")
35
+ end
36
+
37
+ changes
38
+ end
39
+
40
+ private
41
+
42
+ def remove_external_link_elements(document, context)
43
+ changes = []
44
+
45
+ document.traverse do |node|
46
+ next unless element?(node) && node.name == "link"
47
+
48
+ rel = get_attribute(node, "rel")
49
+ href = get_attribute(node, "href")
50
+
51
+ if rel&.downcase == "stylesheet" && href && !allowed_url?(href)
52
+ if @log_removed_elements
53
+ log_removal(context,
54
+ "Removing external CSS link: #{href}")
55
+ end
56
+ node.remove
57
+ changes << {
58
+ type: :element_removed,
59
+ description: "Removed external CSS link: #{href}",
60
+ node_name: node.name,
61
+ href: href,
62
+ }
63
+ end
64
+ end
65
+
66
+ changes
67
+ end
68
+
69
+ def remove_external_imports_from_style_elements(document, context)
70
+ changes = []
71
+
72
+ document.traverse do |node|
73
+ next unless element?(node) && node.name == "style"
74
+
75
+ content = node.text || ""
76
+ original_content = content.dup
77
+
78
+ # Remove @import url() statements
79
+ content.gsub!(/@import\s+url\s*\(\s*['"]?([^'")\s]+)['"]?\s*\)\s*;?/i) do |match|
80
+ url = ::Regexp.last_match(1)
81
+ if allowed_url?(url)
82
+ match # Keep allowed URLs
83
+ else
84
+ if @log_removed_elements
85
+ log_removal(context,
86
+ "Removing external CSS import: #{url}")
87
+ end
88
+ changes << {
89
+ type: :content_modified,
90
+ description: "Removed external CSS import: #{url}",
91
+ node_name: node.name,
92
+ url: url,
93
+ }
94
+ "" # Remove disallowed URLs
95
+ end
96
+ end
97
+
98
+ # Remove @import "url" statements
99
+ content.gsub!(/@import\s+['"]([^'"]+)['"]\s*;?/i) do |match|
100
+ url = ::Regexp.last_match(1)
101
+ if allowed_url?(url)
102
+ match # Keep allowed URLs
103
+ else
104
+ if @log_removed_elements
105
+ log_removal(context,
106
+ "Removing external CSS import: #{url}")
107
+ end
108
+ changes << {
109
+ type: :content_modified,
110
+ description: "Removed external CSS import: #{url}",
111
+ node_name: node.name,
112
+ url: url,
113
+ }
114
+ "" # Remove disallowed URLs
115
+ end
116
+ end
117
+
118
+ # Update the node content if it changed
119
+ if content != original_content
120
+ # Clear existing text content and set new content
121
+ node.text = content
122
+ end
123
+ end
124
+
125
+ changes
126
+ end
127
+
128
+ def remove_external_urls_from_style_attributes(document, context)
129
+ changes = []
130
+
131
+ document.traverse do |node|
132
+ next unless element?(node)
133
+
134
+ style_value = get_attribute(node, "style")
135
+ next unless style_value
136
+
137
+ original_style = style_value.dup
138
+
139
+ # Remove url() references in style attributes
140
+ style_value.gsub!(/url\s*\(\s*['"]?([^'")\s]+)['"]?\s*\)/i) do |match|
141
+ url = ::Regexp.last_match(1)
142
+ if allowed_url?(url)
143
+ match # Keep allowed URLs
144
+ else
145
+ if @log_removed_elements
146
+ log_removal(context,
147
+ "Removing external URL from style attribute: #{url}")
148
+ end
149
+ changes << {
150
+ type: :attribute_modified,
151
+ description: "Removed external URL from style attribute: #{url}",
152
+ node_name: node.name,
153
+ attribute_name: "style",
154
+ url: url,
155
+ }
156
+ "" # Remove disallowed URLs
157
+ end
158
+ end
159
+
160
+ # Update the attribute if it changed
161
+ if style_value != original_style
162
+ set_attribute(node, "style",
163
+ style_value)
164
+ end
165
+ end
166
+
167
+ changes
168
+ end
169
+
170
+ def allowed_url?(url)
171
+ return true if url.nil? || url.empty?
172
+
173
+ # Data URLs are typically allowed
174
+ return true if url.start_with?("data:")
175
+
176
+ # Fragment identifiers (internal references) are allowed
177
+ return true if url.start_with?("#")
178
+
179
+ # For remediation, we're more restrictive - only allow data: and fragment URLs
180
+ false
181
+ end
182
+
183
+ def log_removal(context, message)
184
+ context.add_info(message: message) if context.respond_to?(:add_info)
185
+ end
186
+
187
+ def log_remediation(context, message)
188
+ context.add_info(message: message) if context.respond_to?(:add_info)
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_remediation"
4
+
5
+ module SvgConform
6
+ module Remediations
7
+ # Promotes CSS style properties to individual attributes
8
+ class StylePromotionRemediation < BaseRemediation
9
+ # CSS properties that can be promoted to SVG attributes
10
+ PROMOTABLE_PROPERTIES = %w[
11
+ fill stroke stroke-width stroke-opacity stroke-linecap stroke-linejoin
12
+ fill-opacity fill-rule text-anchor font-family font-size font-weight
13
+ font-style opacity visibility display
14
+ ].freeze
15
+
16
+ def apply(document, _context)
17
+ results = []
18
+
19
+ document.xpath("//*[@style]").each do |element|
20
+ style_attr = element["style"]
21
+ next if style_attr.nil? || style_attr.strip.empty?
22
+
23
+ promoted_properties = promote_style_properties(element, style_attr)
24
+ promoted_properties.each do |property, value|
25
+ results << create_promotion_result(element, property, value)
26
+ end
27
+ end
28
+
29
+ results
30
+ end
31
+
32
+ private
33
+
34
+ def promote_style_properties(element, style_attr)
35
+ promoted = {}
36
+ remaining_styles = []
37
+
38
+ # Parse CSS style declarations
39
+ style_declarations = parse_style_declarations(style_attr)
40
+
41
+ style_declarations.each do |property, value|
42
+ if PROMOTABLE_PROPERTIES.include?(property)
43
+ # Promote to attribute
44
+ element[property] = value
45
+ promoted[property] = value
46
+ else
47
+ # Keep in style attribute
48
+ remaining_styles << "#{property}:#{value}"
49
+ end
50
+ end
51
+
52
+ # Update or remove style attribute
53
+ if remaining_styles.empty?
54
+ element.remove_attribute("style")
55
+ else
56
+ element["style"] = remaining_styles.join(";")
57
+ end
58
+
59
+ promoted
60
+ end
61
+
62
+ def parse_style_declarations(style_attr)
63
+ declarations = {}
64
+
65
+ # Split by semicolon and parse each declaration
66
+ style_attr.split(";").each do |declaration|
67
+ next if declaration.strip.empty?
68
+
69
+ parts = declaration.split(":", 2)
70
+ next unless parts.length == 2
71
+
72
+ property = parts[0].strip
73
+ value = parts[1].strip
74
+
75
+ # Remove any trailing semicolon from value
76
+ value = value.chomp(";")
77
+
78
+ declarations[property] = value
79
+ end
80
+
81
+ declarations
82
+ end
83
+
84
+ def create_promotion_result(element, property, _value)
85
+ log_change(
86
+ :style_promotion,
87
+ "Style property '#{property}' promoted to attribute",
88
+ element,
89
+ )
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_remediation"
4
+
5
+ module SvgConform
6
+ module Remediations
7
+ # Remediation action for viewBox-related issues that matches svgcheck behavior
8
+ class ViewboxRemediation < BaseRemediation
9
+ attribute :type, :string, default: -> { "ViewboxRemediation" }
10
+
11
+ def apply(document, _context)
12
+ changes = []
13
+
14
+ # Get the root SVG element directly from the document
15
+ svg_element = document.root
16
+
17
+ if svg_element
18
+ # Check if viewBox is missing or malformed
19
+ current_viewbox = get_attribute(svg_element, "viewBox")
20
+
21
+ if current_viewbox.nil? || current_viewbox.empty?
22
+ changes.concat(add_viewbox_from_dimensions(svg_element))
23
+ elsif current_viewbox =~ /[()]/ || current_viewbox !~ /^[\d.\s-]+$/
24
+ # Malformed viewBox - try to fix it
25
+ changes.concat(fix_malformed_viewbox(svg_element, current_viewbox))
26
+ end
27
+ end
28
+
29
+ changes
30
+ end
31
+
32
+ private
33
+
34
+ def add_viewbox_from_dimensions(svg_element)
35
+ changes = []
36
+
37
+ # Try to get width and height attributes (matching svgcheck logic)
38
+ width = get_attribute(svg_element, "width")
39
+ height = get_attribute(svg_element, "height")
40
+
41
+ if width && height
42
+ # Extract numeric values (svgcheck uses maybefloat function)
43
+ width_num = extract_float(width)
44
+ height_num = extract_float(height)
45
+
46
+ if width_num && height_num
47
+ # Create viewBox value: "0 0 width height" (matching svgcheck format)
48
+ # Format numbers to avoid unnecessary decimal places
49
+ width_formatted = width_num == width_num.to_i ? width_num.to_i.to_s : width_num.to_s
50
+ height_formatted = height_num == height_num.to_i ? height_num.to_i.to_s : height_num.to_s
51
+ viewbox_value = "0 0 #{width_formatted} #{height_formatted}"
52
+ set_attribute(svg_element, "viewBox", viewbox_value)
53
+
54
+ changes << log_change(
55
+ :attribute_added,
56
+ "The attribute viewBox is required on the root svg element",
57
+ svg_element,
58
+ )
59
+ changes << log_change(
60
+ :attribute_added,
61
+ "Trying to put in the attribute with value '#{viewbox_value}'",
62
+ svg_element,
63
+ )
64
+ end
65
+ end
66
+
67
+ changes
68
+ end
69
+
70
+ def extract_float(value)
71
+ return nil unless value
72
+
73
+ # Try to convert to float (matching svgcheck's maybefloat function)
74
+ begin
75
+ Float(value)
76
+ rescue ArgumentError, TypeError
77
+ nil
78
+ end
79
+ end
80
+
81
+ def fix_malformed_viewbox(svg_element, malformed_value)
82
+ changes = []
83
+
84
+ # Remove parentheses and extra characters, extract numbers
85
+ cleaned_value = malformed_value.gsub(/[()]/, "").strip
86
+
87
+ # Split by spaces or commas and extract numeric values
88
+ numbers = cleaned_value.split(/[\s,]+/).map do |part|
89
+ part.gsub(/[^\d.-]/, "") # Remove non-numeric characters except . and -
90
+ end.compact.reject(&:empty?)
91
+
92
+ # Try to convert to valid numbers
93
+ valid_numbers = numbers.map do |num_str|
94
+ Float(num_str)
95
+ rescue ArgumentError
96
+ nil
97
+ end.compact
98
+
99
+ # If we have 4 valid numbers, create proper viewBox
100
+ if valid_numbers.length == 4
101
+ proper_viewbox = valid_numbers.join(" ")
102
+ set_attribute(svg_element, "viewBox", proper_viewbox)
103
+
104
+ changes << log_change(
105
+ :attribute_modified,
106
+ "Fixed malformed viewBox attribute from '#{malformed_value}' to '#{proper_viewbox}'",
107
+ svg_element,
108
+ )
109
+ elsif valid_numbers.length >= 2
110
+ # If we have at least width and height, use them
111
+ width = valid_numbers[2] || valid_numbers[0]
112
+ height = valid_numbers[3] || valid_numbers[1]
113
+ proper_viewbox = "0 0 #{width} #{height}"
114
+ set_attribute(svg_element, "viewBox", proper_viewbox)
115
+
116
+ changes << log_change(
117
+ :attribute_modified,
118
+ "Fixed malformed viewBox attribute from '#{malformed_value}' to '#{proper_viewbox}'",
119
+ svg_element,
120
+ )
121
+ end
122
+
123
+ changes
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "remediations/base_remediation"
4
+ require_relative "remediations/color_remediation"
5
+ require_relative "remediations/font_embedding_remediation"
6
+ require_relative "remediations/font_remediation"
7
+ require_relative "remediations/image_embedding_remediation"
8
+ require_relative "remediations/invalid_id_references_remediation"
9
+ require_relative "remediations/namespace_attribute_remediation"
10
+ require_relative "remediations/namespace_remediation"
11
+ require_relative "remediations/no_external_css_remediation"
12
+ require_relative "remediations/style_promotion_remediation"
13
+ require_relative "remediations/viewbox_remediation"
14
+
15
+ module SvgConform
16
+ module Remediations
17
+ # Auto-load all remediation classes
18
+ def self.all
19
+ [
20
+ ColorRemediation,
21
+ FontEmbeddingRemediation,
22
+ FontRemediation,
23
+ ImageEmbeddingRemediation,
24
+ InvalidIdReferencesRemediation,
25
+ NamespaceAttributeRemediation,
26
+ NamespaceRemediation,
27
+ NoExternalCSSRemediation,
28
+ StylePromotionRemediation,
29
+ ViewboxRemediation,
30
+ ]
31
+ end
32
+
33
+ # Find a remediation class by name
34
+ def self.find(class_name)
35
+ const_get(class_name)
36
+ rescue NameError
37
+ nil
38
+ end
39
+ end
40
+ end