From 5b916b3c3cb93582eb0cbfccdf6a14e2d4deea65 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Mon, 24 Jan 2022 00:01:28 +0800 Subject: [PATCH] feat(image): implement svg_min --- .gitignore | 3 ++ optimize-test.js | 57 ++++++++++++++++++++- packages/binding/Cargo.toml | 2 + packages/binding/index.d.ts | 13 +++++ packages/binding/index.js | 4 +- packages/binding/src/lib.rs | 74 +++++++++++++++++++++++++++ packages/rollup-plugin/lib/index.d.ts | 0 packages/rollup-plugin/lib/index.js | 1 - 8 files changed, 151 insertions(+), 3 deletions(-) delete mode 100644 packages/rollup-plugin/lib/index.d.ts delete mode 100644 packages/rollup-plugin/lib/index.js 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";