Skip to content

Commit

Permalink
feat(oxvg_optimiser): #39 remove unused ns
Browse files Browse the repository at this point in the history
  • Loading branch information
noahbald committed Jan 12, 2025
1 parent f3a7e77 commit 9788fb4
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/oxvg_optimiser/src/jobs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ jobs! {
convert_transform: ConvertTransform (is_default: true),
remove_empty_attrs: RemoveEmptyAttrs (is_default: true),
remove_empty_containers: RemoveEmptyContainers (is_default: true),
remove_unused_n_s: RemoveUnusedNS (is_default: true),
merge_paths: MergePaths (is_default: true),
sort_attrs: SortAttrs (is_default: true),
sort_defs_children: SortDefsChildren (is_default: true),
Expand Down
201 changes: 201 additions & 0 deletions crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
use std::collections::HashSet;

use oxvg_ast::{
attribute::{Attr, Attributes},
element::Element,
visitor::{Context, PrepareOutcome, Visitor},
};
use serde::Deserialize;

#[derive(Clone)]
pub struct RemoveUnusedNS {
enabled: bool,
unused_namespaces: HashSet<String>,
}

impl<E: Element> Visitor<E> for RemoveUnusedNS {
type Error = String;

fn prepare(
&mut self,
_document: &E,
_context_flags: &mut oxvg_ast::visitor::ContextFlags,
) -> oxvg_ast::visitor::PrepareOutcome {
if self.enabled {
PrepareOutcome::none
} else {
PrepareOutcome::skip
}
}

fn document(&mut self, document: &mut E) -> Result<(), Self::Error> {
document.for_each_element_child(|e| {
self.root_element(&e);
});
Ok(())
}

fn exit_document(
&mut self,
document: &mut E,
_context: &Context<E>,
) -> Result<(), Self::Error> {
document.for_each_element_child(|e| {
self.exit_root_element(&e);
});
Ok(())
}
}

impl RemoveUnusedNS {
fn root_element<E: Element>(&mut self, element: &E) {
if element.prefix().is_none() && element.local_name().as_ref() == "svg" {
for attr in element.attributes().iter() {
if attr.prefix().is_some_and(|p| p.as_ref() == "xmlns") {
self.unused_namespaces.insert(attr.local_name().to_string());
}
}
}
if self.unused_namespaces.is_empty() {
return;
}
let Some(prefix) = element.prefix().as_ref().map(ToString::to_string) else {
return;
};

self.unused_namespaces.remove(&prefix);
for attr in element.attributes().iter() {
if let Some(prefix) = attr.prefix().as_ref().map(ToString::to_string) {
self.unused_namespaces.remove(&prefix);
}
}
}

fn exit_root_element<E: Element>(&self, element: &E) {
if element.prefix().is_some() || element.local_name().as_ref() != "svg" {
return;
}

for name in &self.unused_namespaces {
log::debug!("removing xmlns:{name}");
element.remove_attribute(&format!("xmlns:{name}").into());
}
}
}

impl Default for RemoveUnusedNS {
fn default() -> Self {
Self {
enabled: true,
unused_namespaces: HashSet::new(),
}
}
}

impl<'de> Deserialize<'de> for RemoveUnusedNS {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let enabled = bool::deserialize(deserializer)?;
Ok(Self {
enabled,
unused_namespaces: HashSet::new(),
})
}
}

#[test]
fn remove_unused_n_s() -> anyhow::Result<()> {
use crate::test_config;

insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="http://trololololololololololo.com/">
<g>
test
</g>
</svg>"#
),
)?);

// FIXME: rcdom removes used xmlns
insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="http://trololololololololololo.com/">
<g test:attr="val">
test
</g>
</svg>"#
),
)?);

// FIXME: rcdom removes used xmlns
insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="http://trololololololololololo.com/" xmlns:test2="http://trololololololololololo.com/">
<g test:attr="val">
<g>
test
</g>
</g>
</svg>"#
),
)?);

// FIXME: rcdom removes used xmlns
insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="http://trololololololololololo.com/" xmlns:test2="http://trololololololololololo.com/">
<g test:attr="val">
<g test2:attr="val">
test
</g>
</g>
</svg>"#
),
)?);

// FIXME: rcdom removes used xmlns
insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="http://trololololololololololo.com/" xmlns:test2="http://trololololololololololo.com/">
<g>
<test:elem>
test
</test:elem>
</g>
</svg>"#
),
)?);

// FIXME: rcdom removes used xmlns
insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="http://trololololololololololo.com/" xmlns:test2="http://trololololololololololo.com/">
<test:elem>
<test2:elem>
test
</test2:elem>
</test:elem>
</svg>"#
),
)?);

insta::assert_snapshot!(test_config(
r#"{ "removeUnusedNS": true }"#,
Some(
r#"<svg xmlns="http://www.w3.org/2000/svg" inkscape:version="0.92.2 (5c3e80d, 2017-08-06)" sodipodi:docname="test.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
test
</svg>"#
),
)?);

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:test=\"http://trololololololololololo.com/\">\n <g test:attr=\"val\">\n test\n </g>\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg">
<g test:attr="val">
test
</g>
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:test=\"http://trololololololololololo.com/\" xmlns:test2=\"http://trololololololololololo.com/\">\n <g test:attr=\"val\">\n <g>\n test\n </g>\n </g>\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg">
<g test:attr="val">
<g>
test
</g>
</g>
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:test=\"http://trololololololololololo.com/\" xmlns:test2=\"http://trololololololololololo.com/\">\n <g test:attr=\"val\">\n <g test2:attr=\"val\">\n test\n </g>\n </g>\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg">
<g test:attr="val">
<g test2:attr="val">
test
</g>
</g>
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:test=\"http://trololololololololololo.com/\" xmlns:test2=\"http://trololololololololololo.com/\">\n <g>\n <test:elem>\n test\n </test:elem>\n </g>\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg">
<g>
<test:elem xmlns:test="http://trololololololololololo.com/">
test
</test:elem>
</g>
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:test=\"http://trololololololololololo.com/\" xmlns:test2=\"http://trololololololololololo.com/\">\n <test:elem>\n <test2:elem>\n test\n </test2:elem>\n </test:elem>\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg">
<test:elem xmlns:test="http://trololololololololololo.com/">
<test2:elem xmlns:test2="http://trololololololololololo.com/">
test
</test2:elem>
</test:elem>
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" inkscape:version=\"0.92.2 (5c3e80d, 2017-08-06)\" sodipodi:docname=\"test.svg\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\">\n test\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg" inkscape:version="0.92.2 (5c3e80d, 2017-08-06)" sodipodi:docname="test.svg">
test
</svg>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs
expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:test=\"http://trololololololololololo.com/\">\n <g>\n test\n </g>\n</svg>\"#),)?"
---
<svg xmlns="http://www.w3.org/2000/svg">
<g>
test
</g>
</svg>

0 comments on commit 9788fb4

Please sign in to comment.