diff --git a/.gitignore b/.gitignore
index 67688c2..dd4cdfa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,7 @@ Cargo.lock
.turbo
*.tsbuildinfo
optimized-lossless.*
+optimized.svg
quantized.png
+lib
+dist
diff --git a/optimize-test.js b/optimize-test.js
index 4ec5eb4..7b78a67 100644
--- a/optimize-test.js
+++ b/optimize-test.js
@@ -1,11 +1,66 @@
const { readFileSync, writeFileSync } = require('fs')
-const { losslessCompressPng, compressJpeg, pngQuantize } = require('./packages/binding')
+const { losslessCompressPng, compressJpeg, pngQuantize, svgMin } = require('./packages/binding')
const PNG = readFileSync('./un-optimized.png')
+const SVG = `
+
+
+
+`
writeFileSync('optimized-lossless.png', losslessCompressPng(PNG))
writeFileSync('quantized.png', pngQuantize(PNG))
writeFileSync('optimized-lossless.jpg', compressJpeg(readFileSync('./un-optimized.jpg')))
+
+writeFileSync('optimized.svg', svgMin(SVG))
diff --git a/packages/binding/Cargo.toml b/packages/binding/Cargo.toml
index 31354d8..6b80f67 100644
--- a/packages/binding/Cargo.toml
+++ b/packages/binding/Cargo.toml
@@ -17,6 +17,8 @@ libc = "0.2"
lodepng = "3"
napi = {version = "2", default-features = false, features = ["napi3"]}
napi-derive = {version = "2", default-features = false, features = ["type-def"]}
+usvg = {version = "0.20", features = ["export"]}
+xmlwriter = "0.1"
[dependencies.oxipng]
default-features = false
diff --git a/packages/binding/index.d.ts b/packages/binding/index.d.ts
index 658574a..d0f8f2b 100644
--- a/packages/binding/index.d.ts
+++ b/packages/binding/index.d.ts
@@ -71,3 +71,16 @@ export interface PngQuantOptions {
posterization?: number | undefined | null
}
export function pngQuantize(input: Buffer, options?: PngQuantOptions | undefined | null): Buffer
+export const enum Ident {
+ None = 0,
+ Two = 2,
+ Four = 4,
+ Tab = 5
+}
+export interface SvgMinOptions {
+ idPrefix?: string | undefined | null
+ useSingleQuote?: boolean | undefined | null
+ indent?: Ident | undefined | null
+ attributesIndent?: Ident | undefined | null
+}
+export function svgMin(input: string | Buffer, options?: SvgMinOptions | undefined | null): string
diff --git a/packages/binding/index.js b/packages/binding/index.js
index 4485cbf..6ffd8af 100644
--- a/packages/binding/index.js
+++ b/packages/binding/index.js
@@ -221,8 +221,10 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
-const { losslessCompressPng, compressJpeg, pngQuantize } = nativeBinding
+const { losslessCompressPng, compressJpeg, pngQuantize, Ident, svgMin } = nativeBinding
module.exports.losslessCompressPng = losslessCompressPng
module.exports.compressJpeg = compressJpeg
module.exports.pngQuantize = pngQuantize
+module.exports.Ident = Ident
+module.exports.svgMin = svgMin
diff --git a/packages/binding/src/lib.rs b/packages/binding/src/lib.rs
index cd12b77..00bde7f 100644
--- a/packages/binding/src/lib.rs
+++ b/packages/binding/src/lib.rs
@@ -260,3 +260,77 @@ pub fn png_quantize(input: Buffer, options: Option) -> Result,
+ pub use_single_quote: Option,
+ pub indent: Option,
+ pub attributes_indent: Option,
+}
+
+#[inline(always)]
+fn ident_to_xml_ident(id: &Ident) -> xmlwriter::Indent {
+ match id {
+ &Ident::None => xmlwriter::Indent::None,
+ &Ident::Two => xmlwriter::Indent::Spaces(2),
+ &Ident::Four => xmlwriter::Indent::Spaces(4),
+ &Ident::Tab => xmlwriter::Indent::Tabs,
+ }
+}
+
+#[napi]
+pub fn svg_min(input: Either, options: Option) -> Result {
+ let (tree, len) = match &input {
+ Either::A(s) => {
+ usvg::Tree::from_str(s.as_str(), &usvg::Options::default().to_ref()).map(|t| (t, s.len()))
+ }
+ Either::B(b) => {
+ usvg::Tree::from_data(b.as_ref(), &usvg::Options::default().to_ref()).map(|t| (t, b.len()))
+ }
+ }
+ .map_err(|err| {
+ Error::new(
+ Status::InvalidArg,
+ format!("Parse svg from input data failed {}", err),
+ )
+ })?;
+ let options = options.unwrap_or(SvgMinOptions {
+ id_prefix: None,
+ use_single_quote: Some(true),
+ indent: Some(Ident::None),
+ attributes_indent: Some(Ident::None),
+ });
+ let result = tree.to_string(&usvg::XmlOptions {
+ id_prefix: options.id_prefix,
+ writer_opts: xmlwriter::Options {
+ use_single_quote: options.use_single_quote.unwrap_or(true),
+ indent: options
+ .indent
+ .as_ref()
+ .map(ident_to_xml_ident)
+ .unwrap_or(xmlwriter::Indent::None),
+ attributes_indent: options
+ .attributes_indent
+ .as_ref()
+ .map(ident_to_xml_ident)
+ .unwrap_or(xmlwriter::Indent::None),
+ },
+ });
+ if result.len() < len {
+ Ok(result)
+ } else {
+ Ok(match input {
+ Either::A(a) => a,
+ Either::B(b) => unsafe { String::from_utf8_unchecked(b.to_vec()) },
+ })
+ }
+}
diff --git a/packages/rollup-plugin/lib/index.d.ts b/packages/rollup-plugin/lib/index.d.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/packages/rollup-plugin/lib/index.js b/packages/rollup-plugin/lib/index.js
deleted file mode 100644
index 3918c74..0000000
--- a/packages/rollup-plugin/lib/index.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";