Skip to content

Commit c262c0d

Browse files
committed
feat(oxvg_optimiser): #25 remove editor ns data
1 parent 4248afa commit c262c0d

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
@@ -154,6 +154,12 @@ pub trait Attributes<'a>: Debug + Clone {
154154

155155
fn sort(&self, order: &[String], xmlns_front: bool);
156156

157+
/// Retains the attributes specified by the predicate.
158+
///
159+
/// In other words, removes all attribute where the closure returns false.
160+
///
161+
/// The arguments provided to the closure are the optional prefix, followed by the local-name,
162+
/// and finally the value.
157163
fn retain<F>(&self, f: F)
158164
where
159165
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
@@ -61,6 +61,7 @@ jobs! {
6161
remove_xml_proc_inst: RemoveXMLProcInst (is_default: true),
6262
remove_comments: RemoveComments (is_default: true),
6363
remove_metadata: RemoveMetadata (is_default: true),
64+
remove_editors_ns_data: RemoveEditorsNSData (is_default: true),
6465
cleanup_attributes: CleanupAttributes (is_default: true),
6566
merge_styles: MergeStyles (is_default: true),
6667
inline_styles: InlineStyles<E> (is_default: true),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use std::{cell::RefCell, collections::BTreeSet};
2+
3+
use oxvg_ast::{
4+
atom::Atom,
5+
attribute::{Attr, Attributes},
6+
element::Element,
7+
visitor::{Context, 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(
24+
&mut self,
25+
document: &mut E,
26+
_context: &Context<'_, '_, E>,
27+
) -> Result<(), Self::Error> {
28+
document.for_each_element_child(|ref e| {
29+
self.collect_svg_namespace(e);
30+
self.remove_editor_attributes(e);
31+
self.remove_editor_element(e);
32+
});
33+
Ok(())
34+
}
35+
}
36+
37+
impl RemoveEditorsNSData {
38+
fn collect_svg_namespace<E: Element>(&self, element: &E) {
39+
if element.local_name().as_ref() != "svg" {
40+
return;
41+
}
42+
43+
let mut prefixes = self.prefixes.borrow_mut();
44+
for xmlns in element
45+
.attributes()
46+
.into_iter()
47+
.filter(|a| a.prefix().as_ref().is_some_and(|p| p.as_ref() == "xmlns"))
48+
{
49+
let value = xmlns.value();
50+
let value = value.as_str();
51+
if !EDITOR_NAMESPACES.contains(value)
52+
&& !self
53+
.additional_namespaces
54+
.as_ref()
55+
.is_some_and(|n| n.contains(value))
56+
{
57+
continue;
58+
}
59+
60+
let name = xmlns.local_name();
61+
log::debug!("Adding {name} to prefixes");
62+
prefixes.insert(name.to_string());
63+
}
64+
}
65+
66+
fn remove_editor_attributes<E: Element>(&self, element: &E) {
67+
let prefixes = self.prefixes.borrow();
68+
element.attributes().retain(|attr| {
69+
let Some(prefix) = attr.prefix() else {
70+
return true;
71+
};
72+
73+
!prefixes.contains(prefix.as_ref())
74+
});
75+
}
76+
77+
fn remove_editor_element<E: Element>(&self, element: &E) {
78+
let Some(prefix) = element.prefix() else {
79+
return;
80+
};
81+
82+
if self.prefixes.borrow().contains(prefix.as_ref()) {
83+
log::debug!("Removing element with prefix: {prefix}");
84+
element.remove();
85+
}
86+
}
87+
}
88+
89+
#[test]
90+
fn remove_editors_ns_data() -> anyhow::Result<()> {
91+
use crate::test_config;
92+
93+
insta::assert_snapshot!(test_config(
94+
r#"{ "removeEditorsNsData": {} }"#,
95+
Some(
96+
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
97+
<sodipodi:namedview>
98+
...
99+
</sodipodi:namedview>
100+
101+
<path d="..." sodipodi:nodetypes="cccc"/>
102+
</svg>"#
103+
),
104+
)?);
105+
106+
insta::assert_snapshot!(test_config(
107+
r#"{ "removeEditorsNsData": {} }"#,
108+
Some(
109+
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd">
110+
<sodipodi:namedview>
111+
...
112+
</sodipodi:namedview>
113+
114+
<path d="..." sodipodi:nodetypes="cccc"/>
115+
</svg>"#
116+
),
117+
)?);
118+
119+
Ok(())
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
source: crates/oxvg_optimiser/src/jobs/remove_editors_ns_data.rs
3+
assertion_line: 93
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+
---
6+
<svg xmlns="http://www.w3.org/2000/svg">
7+
<sodipodi:namedview xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
8+
...
9+
</sodipodi:namedview>
10+
<path d="..." sodipodi:nodetypes="cccc"/>
11+
</svg>

0 commit comments

Comments
 (0)