Skip to content

Commit 30ddc48

Browse files
committed
feat(oxvg_optimiser): #25 remove editor ns data
1 parent f3a7e77 commit 30ddc48

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed

crates/oxvg_ast/src/attribute.rs

+6
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ pub trait Attributes<'a>: Debug + Clone {
127127

128128
fn sort(&self, order: &[String], xmlns_front: bool);
129129

130+
/// Retains the attributes specified by the predicate.
131+
///
132+
/// In other words, removes all attribute where the closure returns false.
133+
///
134+
/// The arguments provided to the closure are the optional prefix, followed by the local-name,
135+
/// and finally the value.
130136
fn retain<F>(&self, f: F)
131137
where
132138
F: FnMut(Self::Attribute) -> bool;

crates/oxvg_collections/src/collections.rs

+24
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,27 @@ pub static PSEUDO_TREE_STRUCTURAL: phf::Set<&'static str> = phf_set!(
573573
pub static PSEUDO_USER_ACTION: phf::Set<&'static str> =
574574
phf_set!("active", "focus-visible", "focus-within", "focus", "hover");
575575
pub static PSEUDO_FUNCTIONAL: phf::Set<&'static str> = phf_set!("is", "not", "where", "has");
576+
pub static EDITOR_NAMESPACES: phf::Set<&'static str> = phf_set!(
577+
"http://creativecommons.org/ns#",
578+
"http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd",
579+
"http://ns.adobe.com/AdobeIllustrator/10.0/",
580+
"http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/",
581+
"http://ns.adobe.com/Extensibility/1.0/",
582+
"http://ns.adobe.com/Flows/1.0/",
583+
"http://ns.adobe.com/GenericCustomNamespace/1.0/",
584+
"http://ns.adobe.com/Graphs/1.0/",
585+
"http://ns.adobe.com/ImageReplacement/1.0/",
586+
"http://ns.adobe.com/SaveForWeb/1.0/",
587+
"http://ns.adobe.com/Variables/1.0/",
588+
"http://ns.adobe.com/XPath/1.0/",
589+
"http://purl.org/dc/elements/1.1/",
590+
"http://schemas.microsoft.com/visio/2003/SVGExtensions/",
591+
"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd",
592+
"http://taptrix.com/vectorillustrator/svg_extensions",
593+
"http://www.bohemiancoding.com/sketch/ns",
594+
"http://www.figma.com/figma/ns",
595+
"http://www.inkscape.org/namespaces/inkscape",
596+
"http://www.serif.com/",
597+
"http://www.vector.evaxdesign.sk",
598+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
599+
);

crates/oxvg_optimiser/src/jobs/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jobs! {
6060
remove_xml_proc_inst: RemoveXMLProcInst (is_default: true),
6161
remove_comments: RemoveComments (is_default: true),
6262
remove_metadata: RemoveMetadata (is_default: true),
63+
remove_editors_ns_data: RemoveEditorsNSData (is_default: true),
6364
cleanup_attributes: CleanupAttributes (is_default: true),
6465
merge_styles: MergeStyles (is_default: true),
6566
inline_styles: InlineStyles<E> (is_default: true),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use std::{cell::RefCell, collections::BTreeSet};
2+
3+
use oxvg_ast::{
4+
atom::Atom,
5+
attribute::{Attr, Attributes},
6+
element::Element,
7+
visitor::Visitor,
8+
};
9+
use oxvg_collections::collections::EDITOR_NAMESPACES;
10+
use serde::Deserialize;
11+
12+
#[derive(Deserialize, Clone, Default)]
13+
#[serde(rename_all = "camelCase")]
14+
pub struct RemoveEditorsNSData {
15+
additional_namespaces: Option<BTreeSet<String>>,
16+
#[serde(skip_deserializing)]
17+
prefixes: RefCell<BTreeSet<String>>,
18+
}
19+
20+
impl<E: Element> Visitor<E> for RemoveEditorsNSData {
21+
type Error = String;
22+
23+
fn document(&mut self, document: &mut E) -> Result<(), Self::Error> {
24+
dbg!(&document);
25+
document.for_each_element_child(|ref e| {
26+
self.collect_svg_namespace(e);
27+
self.remove_editor_attributes(e);
28+
self.remove_editor_element(e);
29+
});
30+
Ok(())
31+
}
32+
}
33+
34+
impl RemoveEditorsNSData {
35+
fn collect_svg_namespace<E: Element>(&self, element: &E) {
36+
if element.local_name() != "svg".into() {
37+
return;
38+
}
39+
40+
let mut prefixes = self.prefixes.borrow_mut();
41+
let xmlns_atom = "xmlns".into();
42+
for xmlns in element
43+
.attributes()
44+
.iter()
45+
.filter(|a| a.prefix().is_some_and(|p| p == xmlns_atom))
46+
{
47+
let value = xmlns.value();
48+
let value = value.as_str();
49+
if !EDITOR_NAMESPACES.contains(value)
50+
&& !self
51+
.additional_namespaces
52+
.as_ref()
53+
.is_some_and(|n| n.contains(value))
54+
{
55+
continue;
56+
}
57+
58+
let name = xmlns.local_name();
59+
log::debug!("Adding {name} to prefixes");
60+
prefixes.insert(name.to_string());
61+
}
62+
}
63+
64+
fn remove_editor_attributes<E: Element>(&self, element: &E) {
65+
let prefixes = self.prefixes.borrow();
66+
element.attributes().retain(|attr| {
67+
let Some(prefix) = attr.prefix() else {
68+
return true;
69+
};
70+
71+
!prefixes.contains(prefix.as_ref())
72+
});
73+
}
74+
75+
fn remove_editor_element<E: Element>(&self, element: &E) {
76+
let Some(prefix) = element.prefix() else {
77+
return;
78+
};
79+
80+
if self.prefixes.borrow().contains(prefix.as_ref()) {
81+
log::debug!("Removing element with prefix: {prefix}");
82+
element.remove();
83+
}
84+
}
85+
}
86+
87+
#[test]
88+
fn remove_editors_ns_data() -> anyhow::Result<()> {
89+
use crate::test_config;
90+
91+
insta::assert_snapshot!(test_config(
92+
r#"{ "removeEditorsNsData": {} }"#,
93+
Some(
94+
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
95+
<sodipodi:namedview>
96+
...
97+
</sodipodi:namedview>
98+
99+
<path d="..." sodipodi:nodetypes="cccc"/>
100+
</svg>"#
101+
),
102+
)?);
103+
104+
insta::assert_snapshot!(test_config(
105+
r#"{ "removeEditorsNsData": {} }"#,
106+
Some(
107+
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd">
108+
<sodipodi:namedview>
109+
...
110+
</sodipodi:namedview>
111+
112+
<path d="..." sodipodi:nodetypes="cccc"/>
113+
</svg>"#
114+
),
115+
)?);
116+
117+
Ok(())
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
source: crates/oxvg_optimiser/src/jobs/remove_editors_ns_data.rs
3+
assertion_line: 91
4+
expression: "test_config(r#\"{ \"removeEditorsNsData\": {} }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\">\n <sodipodi:namedview>\n ...\n </sodipodi:namedview>\n\n <path d=\"...\" sodipodi:nodetypes=\"cccc\"/>\n</svg>\"#),)?"
5+
snapshot_kind: text
6+
---
7+
<svg xmlns="http://www.w3.org/2000/svg">
8+
<sodipodi:namedview xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
9+
...
10+
</sodipodi:namedview>
11+
12+
<path d="..." sodipodi:nodetypes="cccc"></path>
13+
</svg>

0 commit comments

Comments
 (0)