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,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_remediation"
4
+
5
+ module SvgConform
6
+ module Remediations
7
+ # Remediation action for fixing invalid ID references
8
+ # Based on the Lucid SVG fix script behavior
9
+ class InvalidIdReferencesRemediation < BaseRemediation
10
+ attribute :type, :string, default: -> { "InvalidIdReferencesRemediation" }
11
+ attribute :strategy, :string, default: "remove_invalid_use"
12
+ attribute :log_removed_elements, :boolean, default: false
13
+ attribute :create_placeholder_comment, :boolean, default: false
14
+ attribute :preserve_valid_attributes, :boolean, default: true
15
+
16
+ def apply(document, _context)
17
+ changes = []
18
+
19
+ # Find all use elements with invalid href references
20
+ document.traverse do |node|
21
+ next unless node.respond_to?(:name) && node.name == "use"
22
+
23
+ href = get_attribute(node,
24
+ "href") || get_attribute(node, "xlink:href")
25
+ next unless href&.start_with?("#")
26
+
27
+ referenced_id = href[1..] # Remove the #
28
+ next if document.xpath("//*[@id='#{referenced_id}']").any? # ID exists, skip
29
+
30
+ # Handle invalid ID reference
31
+ change = handle_use_element(node,
32
+ { href: href, invalid_id: referenced_id })
33
+ changes << change if change
34
+ end
35
+
36
+ changes
37
+ end
38
+
39
+ private
40
+
41
+ def handle_use_element(node, data)
42
+ case @strategy
43
+ when "remove_invalid_use"
44
+ remove_use_element(node, data)
45
+ when "remove_invalid_href"
46
+ remove_href_attribute(node, data)
47
+ when "replace_with_placeholder"
48
+ replace_with_placeholder(node, data)
49
+ else
50
+ raise "Unknown strategy: #{@strategy}"
51
+ end
52
+ end
53
+
54
+ def handle_other_id_reference(node, data)
55
+ case @strategy
56
+ when "remove_invalid_use", "remove_invalid_href"
57
+ remove_invalid_attribute(node, data)
58
+ when "replace_with_placeholder"
59
+ replace_attribute_with_placeholder(node, data)
60
+ else
61
+ raise "Unknown strategy: #{@strategy}"
62
+ end
63
+ end
64
+
65
+ def remove_use_element(node, data)
66
+ if @create_placeholder_comment
67
+ comment_text = "Removed invalid use element: href=#{data[:href]}, invalid_id=#{data[:invalid_id]}"
68
+ comment = create_comment(node.document, comment_text)
69
+ replace_node(node, comment)
70
+ else
71
+ remove_node(node)
72
+ end
73
+
74
+ log_change(
75
+ :remove_element,
76
+ "Removed use element with invalid ID reference: #{data[:invalid_id]}",
77
+ node,
78
+ )
79
+ end
80
+
81
+ def remove_href_attribute(node, data)
82
+ # Remove both possible href attributes
83
+ remove_attribute(node, "xlink:href")
84
+ remove_attribute(node, "href")
85
+
86
+ log_change(
87
+ :remove_attribute,
88
+ "Removed invalid href attribute referencing: #{data[:invalid_id]}",
89
+ node,
90
+ )
91
+ end
92
+
93
+ def replace_with_placeholder(node, data)
94
+ comment_text = "Invalid use element removed: #{node.to_xml.strip}"
95
+ comment = create_comment(node.document, comment_text)
96
+ replace_node(node, comment)
97
+
98
+ log_change(
99
+ :replace_with_comment,
100
+ "Replaced use element with comment due to invalid ID reference: #{data[:invalid_id]}",
101
+ node,
102
+ )
103
+ end
104
+
105
+ def remove_invalid_attribute(node, data)
106
+ attribute_name = data[:attribute]
107
+ remove_attribute(node, attribute_name)
108
+
109
+ log_change(
110
+ :remove_attribute,
111
+ "Removed #{attribute_name} attribute with invalid ID reference: #{data[:invalid_id]}",
112
+ node,
113
+ )
114
+ end
115
+
116
+ def replace_attribute_with_placeholder(node, data)
117
+ attribute_name = data[:attribute]
118
+ placeholder_value = "none" # Safe fallback value
119
+ set_attribute(node, attribute_name, placeholder_value)
120
+
121
+ log_change(
122
+ :replace_attribute,
123
+ "Replaced #{attribute_name} attribute (invalid ID: #{data[:invalid_id]}) with placeholder: #{placeholder_value}",
124
+ node,
125
+ )
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require_relative "base_remediation"
5
+
6
+ module SvgConform
7
+ module Remediations
8
+ # Remediation for removing disallowed namespace attributes and declarations
9
+ class NamespaceAttributeRemediation < BaseRemediation
10
+ attribute :type, :string, default: -> { "NamespaceAttributeRemediation" }
11
+ attribute :disallowed_namespaces, :string, collection: true, default: -> {
12
+ []
13
+ }
14
+ attribute :allowed_namespaces, :string, collection: true, default: -> {
15
+ []
16
+ }
17
+
18
+ yaml do
19
+ map "disallowed_namespaces", to: :disallowed_namespaces
20
+ map "allowed_namespaces", to: :allowed_namespaces
21
+ end
22
+
23
+ def apply(document, _context)
24
+ changes = []
25
+ removed_namespaces = Set.new
26
+
27
+ # First pass: remove disallowed namespace attributes
28
+ document.traverse do |node|
29
+ next unless element?(node)
30
+
31
+ changes.concat(remove_disallowed_attributes(node, removed_namespaces))
32
+ end
33
+
34
+ # Second pass: remove unused namespace declarations
35
+ document.traverse do |node|
36
+ next unless element?(node)
37
+
38
+ changes.concat(remove_unused_namespace_declarations(node,
39
+ removed_namespaces))
40
+ end
41
+
42
+ changes
43
+ end
44
+
45
+ private
46
+
47
+ def remove_disallowed_attributes(node, removed_namespaces)
48
+ changes = []
49
+
50
+ # Get all attributes that need to be checked
51
+ attributes_to_remove = []
52
+
53
+ if node.respond_to?(:attribute_nodes)
54
+ # Use attribute_nodes if available (Nokogiri style)
55
+ node.attribute_nodes.each do |attr|
56
+ namespace_uri = attr.namespace&.href
57
+ next unless namespace_uri
58
+
59
+ if namespace_disallowed?(namespace_uri)
60
+ attr_name = if attr.namespace&.prefix
61
+ "#{attr.namespace.prefix}:#{attr.name}"
62
+ else
63
+ attr.name
64
+ end
65
+ attributes_to_remove << attr_name
66
+ removed_namespaces.add(namespace_uri)
67
+ end
68
+ end
69
+ elsif node.respond_to?(:attributes)
70
+ # Fallback to attributes method
71
+ attributes = node.attributes
72
+
73
+ if attributes.respond_to?(:each_key)
74
+ # Hash case
75
+ attributes.each_key do |name|
76
+ if should_remove_attribute_by_name?(name.to_s, node,
77
+ removed_namespaces)
78
+ attributes_to_remove << name.to_s
79
+ end
80
+ end
81
+ elsif attributes.respond_to?(:each)
82
+ # Array case
83
+ attributes.each do |attr|
84
+ if should_remove_moxml_attribute?(attr, node, removed_namespaces)
85
+ attr_name = attr.respond_to?(:name) ? attr.name : attr.to_s
86
+ attributes_to_remove << attr_name
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # Remove the identified attributes
93
+ attributes_to_remove.each do |attr_name|
94
+ if remove_attribute(node, attr_name)
95
+ changes << {
96
+ type: :attribute_removed,
97
+ description: "Removed disallowed namespace attribute '#{attr_name}'",
98
+ node_name: node.name,
99
+ attribute: attr_name,
100
+ }
101
+ end
102
+ end
103
+
104
+ changes
105
+ end
106
+
107
+ def remove_unused_namespace_declarations(node, removed_namespaces)
108
+ changes = []
109
+
110
+ # Look for xmlns declarations that correspond to removed namespaces
111
+ attributes_to_remove = []
112
+
113
+ if node.respond_to?(:attribute_nodes)
114
+ node.attribute_nodes.each do |attr|
115
+ attr_name = attr.name
116
+ next unless attr_name.start_with?("xmlns:")
117
+
118
+ namespace_uri = attr.value
119
+ # Also check if the namespace prefix itself was in disallowed_namespaces
120
+ # since xmlns:lucid="lucid" means the URI is literally "lucid"
121
+ prefix = attr_name.sub("xmlns:", "")
122
+ if removed_namespaces.include?(namespace_uri) || disallowed_namespaces.include?(namespace_uri) || disallowed_namespaces.include?(prefix)
123
+ attributes_to_remove << attr_name
124
+ end
125
+ end
126
+ elsif node.respond_to?(:attributes)
127
+ attributes = node.attributes
128
+
129
+ if attributes.respond_to?(:each_key)
130
+ attributes.each_key do |name|
131
+ name_str = name.to_s
132
+ next unless name_str.start_with?("xmlns:")
133
+
134
+ namespace_uri = get_attribute(node, name_str)
135
+ prefix = name_str.sub("xmlns:", "")
136
+ if namespace_uri && (removed_namespaces.include?(namespace_uri) || disallowed_namespaces.include?(namespace_uri) || disallowed_namespaces.include?(prefix))
137
+ attributes_to_remove << name_str
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ # Remove the xmlns declarations
144
+ attributes_to_remove.each do |attr_name|
145
+ if remove_attribute(node, attr_name)
146
+ changes << {
147
+ type: :attribute_removed,
148
+ description: "Removed unused namespace declaration '#{attr_name}'",
149
+ node_name: node.name,
150
+ attribute: attr_name,
151
+ }
152
+ end
153
+ end
154
+
155
+ changes
156
+ end
157
+
158
+ def namespace_disallowed?(namespace_uri)
159
+ if allowed_namespaces.empty?
160
+ # Blacklist mode: disallowed namespaces are forbidden
161
+ disallowed_namespaces.include?(namespace_uri)
162
+ else
163
+ # Whitelist mode: only allowed namespaces are permitted
164
+ !allowed_namespaces.include?(namespace_uri)
165
+ end
166
+ end
167
+
168
+ def should_remove_attribute_by_name?(name, node, removed_namespaces)
169
+ # Check if this is a namespaced attribute by looking for colon in name
170
+ return false unless name.include?(":")
171
+
172
+ prefix, = name.split(":", 2)
173
+
174
+ # Find the namespace URI for this prefix
175
+ namespace_uri = find_namespace_uri(node, prefix)
176
+
177
+ return false unless namespace_uri
178
+
179
+ if namespace_disallowed?(namespace_uri)
180
+ removed_namespaces.add(namespace_uri)
181
+ true
182
+ else
183
+ false
184
+ end
185
+ end
186
+
187
+ def should_remove_moxml_attribute?(attr, node, removed_namespaces)
188
+ if attr.respond_to?(:namespace) && attr.namespace
189
+ namespace_uri = if attr.namespace.respond_to?(:href)
190
+ attr.namespace.href
191
+ elsif attr.namespace.respond_to?(:uri)
192
+ attr.namespace.uri
193
+ else
194
+ attr.namespace.to_s
195
+ end
196
+
197
+ return false unless namespace_uri && !namespace_uri.empty?
198
+
199
+ if namespace_disallowed?(namespace_uri)
200
+ removed_namespaces.add(namespace_uri)
201
+ true
202
+ else
203
+ false
204
+ end
205
+ else
206
+ # Fallback to name-based checking
207
+ name = attr.respond_to?(:name) ? attr.name : attr.to_s
208
+ should_remove_attribute_by_name?(name, node, removed_namespaces)
209
+ end
210
+ end
211
+
212
+ def find_namespace_uri(node, prefix)
213
+ # Check current node's namespace definitions
214
+ current = node
215
+ while current.respond_to?(:parent)
216
+ if current.respond_to?(:namespace_definitions)
217
+ ns_def = current.namespace_definitions.find do |ns|
218
+ ns.prefix == prefix
219
+ end
220
+ if ns_def
221
+ return ns_def.uri if ns_def.respond_to?(:uri)
222
+ return ns_def.href if ns_def.respond_to?(:href)
223
+
224
+ return ns_def.to_s
225
+ end
226
+ end
227
+
228
+ # Check if it's defined as an attribute (xmlns:prefix)
229
+ xmlns_value = get_attribute(current, "xmlns:#{prefix}")
230
+ return xmlns_value if xmlns_value
231
+
232
+ # Move to parent, but break if parent is nil or doesn't respond to parent
233
+ begin
234
+ current = current.parent
235
+ rescue StandardError
236
+ break
237
+ end
238
+ end
239
+
240
+ nil
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_remediation"
4
+
5
+ module SvgConform
6
+ module Remediations
7
+ # Remediation action for namespace-related issues
8
+ class NamespaceRemediation < BaseRemediation
9
+ attribute :type, :string, default: -> { "NamespaceRemediation" }
10
+ attribute :default_namespace, :string, default: "http://www.w3.org/2000/svg"
11
+ attribute :allowed_namespaces, :string, collection: true, default: -> {
12
+ ["http://www.w3.org/2000/svg", "http://www.w3.org/1999/xlink",
13
+ "http://www.w3.org/XML/1998/namespace"]
14
+ }
15
+ attribute :remove_elements, :boolean, default: true
16
+ attribute :remove_attributes, :boolean, default: true
17
+ attribute :remove_declarations, :boolean, default: true
18
+
19
+ yaml do
20
+ map "default_namespace", to: :default_namespace
21
+ map "allowed_namespaces", to: :allowed_namespaces
22
+ map "remove_elements", to: :remove_elements
23
+ map "remove_attributes", to: :remove_attributes
24
+ map "remove_declarations", to: :remove_declarations
25
+ end
26
+ def apply(document, _context)
27
+ changes = []
28
+ default_namespace
29
+
30
+ # Skip xmlns handling - assume it's already correct in most cases
31
+ # (This avoids duplicate xmlns attribute issues)
32
+
33
+ # Remove invalid namespace elements and attributes based on configuration
34
+ nodes_to_remove = []
35
+
36
+ document.traverse do |node|
37
+ next unless element?(node)
38
+
39
+ # Check if element has invalid namespace using Moxml's namespace API
40
+ if remove_elements && node.respond_to?(:namespace) && node.namespace
41
+ namespace_uri = get_namespace_uri(node.namespace)
42
+
43
+ if namespace_uri && !allowed_namespaces.include?(namespace_uri)
44
+ nodes_to_remove << node
45
+ changes << {
46
+ type: :element_removed,
47
+ description: "Removed invalid namespace element: #{node.name} (#{namespace_uri})",
48
+ node_name: node.name,
49
+ namespace_uri: namespace_uri,
50
+ }
51
+ next # Skip attribute checking for elements we're removing
52
+ end
53
+ end
54
+
55
+ # Remove invalid namespace attributes from all elements
56
+ if remove_attributes && node.respond_to?(:attributes) && node.attributes
57
+ invalid_attrs = []
58
+
59
+ node.attributes.each do |attr_name, attr_value|
60
+ # Handle both string keys and Moxml::Attribute objects
61
+ name_str = attr_name.respond_to?(:name) ? attr_name.name : attr_name.to_s
62
+
63
+ # For root SVG element, remove xmlns declarations for disallowed namespaces
64
+ if remove_declarations && node.name == "svg" && name_str.start_with?("xmlns:")
65
+ name_str.sub("xmlns:", "")
66
+ value_str = attr_value.respond_to?(:value) ? attr_value.value : attr_value.to_s
67
+
68
+ if !allowed_namespaces.include?(value_str)
69
+ invalid_attrs << name_str
70
+ end
71
+ end
72
+
73
+ # For all elements, remove attributes with invalid namespaces
74
+ # Check if attribute has namespace information (Moxml::Attribute objects)
75
+ if attr_name.respond_to?(:namespace) && attr_name.namespace
76
+ attr_namespace_uri = get_namespace_uri(attr_name.namespace)
77
+
78
+ if attr_namespace_uri && !allowed_namespaces.include?(attr_namespace_uri)
79
+ invalid_attrs << name_str
80
+ end
81
+ end
82
+ end
83
+
84
+ # Remove invalid attributes
85
+ invalid_attrs.each do |attr_name|
86
+ remove_attribute(node, attr_name)
87
+ description = if attr_name.start_with?("xmlns:")
88
+ "Removed invalid namespace declaration: #{attr_name}"
89
+ else
90
+ "Removed invalid namespace attribute: #{attr_name}"
91
+ end
92
+
93
+ changes << {
94
+ type: :attribute_removed,
95
+ description: description,
96
+ node_name: node.name,
97
+ attribute_name: attr_name,
98
+ }
99
+ end
100
+ end
101
+ end
102
+
103
+ # Remove invalid namespace elements
104
+ nodes_to_remove.each do |node|
105
+ remove_node(node)
106
+ end
107
+
108
+ changes
109
+ end
110
+
111
+ private
112
+
113
+ def get_namespace_uri(namespace)
114
+ # Extract URI from Moxml::Namespace object
115
+ if namespace.respond_to?(:uri)
116
+ namespace.uri
117
+ elsif namespace.respond_to?(:to_s)
118
+ # Extract URI from string representation if needed
119
+ uri_match = namespace.to_s.match(/="([^"]+)"/)
120
+ uri_match ? uri_match[1] : nil
121
+ else
122
+ nil
123
+ end
124
+ end
125
+
126
+ def find_namespace_uri_for_prefix(node, prefix)
127
+ # Check current node and ancestors for namespace declarations
128
+ current = node
129
+ while current
130
+ # Check for xmlns:prefix attribute
131
+ xmlns_attr = "xmlns:#{prefix}"
132
+ return current.attributes[xmlns_attr] if current.respond_to?(:attributes) && current.attributes[xmlns_attr]
133
+
134
+ # Check using get_attribute method
135
+ namespace_uri = get_attribute(current, xmlns_attr)
136
+ return namespace_uri if namespace_uri
137
+
138
+ # Move to parent
139
+ current = current.respond_to?(:parent) ? current.parent : nil
140
+ end
141
+
142
+ nil
143
+ end
144
+
145
+ def find_namespace_uri_for_attribute(node, prefix)
146
+ # Same logic as find_namespace_uri_for_prefix
147
+ find_namespace_uri_for_prefix(node, prefix)
148
+ end
149
+ end
150
+ end
151
+ end