Minify SVG element whitespace
This commit is contained in:
parent
04707e5e06
commit
737a82df40
|
@ -1,5 +1,9 @@
|
|||
# minify-html changelog
|
||||
|
||||
## 0.8.0
|
||||
|
||||
- Minify whitespace in SVG elements.
|
||||
|
||||
## 0.7.2
|
||||
|
||||
- Fix Node.js library build process on Windows.
|
||||
|
|
|
@ -288,7 +288,7 @@ Remove any leading/trailing whitespace from any leading/trailing text nodes of a
|
|||
|
||||
#### Element types
|
||||
|
||||
minify-html recognises elements based on one of a few ways it assumes they are used. By making these assumptions, it can apply optimal whitespace minification strategies.
|
||||
minify-html assumes HTML and SVG elements are used in specific ways, based on standards and best practices. By making these assumptions, it can apply optimal whitespace minification strategies. If these assumptions do not hold, consider adjusting the HTML source or turning off whitespace minification.
|
||||
|
||||
|Group|Elements|Expected children|
|
||||
|---|---|---|
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::common::spec::tag::ns::Namespace;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -44,14 +45,22 @@ static ROOT: &WhitespaceMinification = &WhitespaceMinification {
|
|||
trim: true,
|
||||
};
|
||||
|
||||
static DEFAULT: &WhitespaceMinification = &WhitespaceMinification {
|
||||
static DEFAULT_HTML: &WhitespaceMinification = &WhitespaceMinification {
|
||||
collapse: true,
|
||||
destroy_whole: false,
|
||||
trim: false,
|
||||
};
|
||||
|
||||
// SVG 2 spec requires unknown elements to be treated like <g>:
|
||||
// https://www.w3.org/TR/SVG2/struct.html#UnknownElement.
|
||||
static DEFAULT_SVG: &WhitespaceMinification = &WhitespaceMinification {
|
||||
collapse: true,
|
||||
destroy_whole: true,
|
||||
trim: true,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref TAG_WHITESPACE_MINIFICATION: HashMap<&'static [u8], &'static WhitespaceMinification> = {
|
||||
static ref HTML_TAG_WHITESPACE_MINIFICATION: HashMap<&'static [u8], &'static WhitespaceMinification> = {
|
||||
let mut m = HashMap::<&'static [u8], &'static WhitespaceMinification>::new();
|
||||
// Content tags.
|
||||
m.insert(b"address", CONTENT);
|
||||
|
@ -164,20 +173,126 @@ lazy_static! {
|
|||
|
||||
m
|
||||
};
|
||||
|
||||
static ref SVG_TAG_WHITESPACE_MINIFICATION: HashMap<&'static [u8], &'static WhitespaceMinification> = {
|
||||
let mut m = HashMap::<&'static [u8], &'static WhitespaceMinification>::new();
|
||||
|
||||
// Content tags.
|
||||
m.insert(b"desc", CONTENT);
|
||||
m.insert(b"text", CONTENT);
|
||||
m.insert(b"title", CONTENT);
|
||||
|
||||
// Formatting tags.
|
||||
m.insert(b"a", FORMATTING);
|
||||
m.insert(b"altGlyph", FORMATTING);
|
||||
m.insert(b"tspan", FORMATTING);
|
||||
m.insert(b"textPath", FORMATTING);
|
||||
m.insert(b"tref", FORMATTING);
|
||||
|
||||
// Layout tags.
|
||||
m.insert(b"altGlyphDef", LAYOUT);
|
||||
m.insert(b"altGlyphItem", LAYOUT);
|
||||
m.insert(b"animate", LAYOUT);
|
||||
m.insert(b"animateColor", LAYOUT);
|
||||
m.insert(b"animateMotion", LAYOUT);
|
||||
m.insert(b"animateTransform", LAYOUT);
|
||||
m.insert(b"circle", LAYOUT);
|
||||
m.insert(b"clipPath", LAYOUT);
|
||||
m.insert(b"cursor", LAYOUT);
|
||||
m.insert(b"defs", LAYOUT);
|
||||
m.insert(b"discard", LAYOUT);
|
||||
m.insert(b"ellipse", LAYOUT);
|
||||
m.insert(b"feBlend", LAYOUT);
|
||||
m.insert(b"feColorMatrix", LAYOUT);
|
||||
m.insert(b"feComponentTransfer", LAYOUT);
|
||||
m.insert(b"feComposite", LAYOUT);
|
||||
m.insert(b"feConvolveMatrix", LAYOUT);
|
||||
m.insert(b"feDiffuseLighting", LAYOUT);
|
||||
m.insert(b"feDisplacementMap", LAYOUT);
|
||||
m.insert(b"feDistantLight", LAYOUT);
|
||||
m.insert(b"feDropShadow", LAYOUT);
|
||||
m.insert(b"feFlood", LAYOUT);
|
||||
m.insert(b"feFuncA", LAYOUT);
|
||||
m.insert(b"feFuncB", LAYOUT);
|
||||
m.insert(b"feFuncG", LAYOUT);
|
||||
m.insert(b"feFuncR", LAYOUT);
|
||||
m.insert(b"feGaussianBlur", LAYOUT);
|
||||
m.insert(b"feImage", LAYOUT);
|
||||
m.insert(b"feMerge", LAYOUT);
|
||||
m.insert(b"feMergeNode", LAYOUT);
|
||||
m.insert(b"feMorphology", LAYOUT);
|
||||
m.insert(b"feOffset", LAYOUT);
|
||||
m.insert(b"fePointLight", LAYOUT);
|
||||
m.insert(b"feSpecularLighting", LAYOUT);
|
||||
m.insert(b"feSpotLight", LAYOUT);
|
||||
m.insert(b"feTile", LAYOUT);
|
||||
m.insert(b"feTurbulence", LAYOUT);
|
||||
m.insert(b"filter", LAYOUT);
|
||||
m.insert(b"font-face-format", LAYOUT);
|
||||
m.insert(b"font-face-name", LAYOUT);
|
||||
m.insert(b"font-face-src", LAYOUT);
|
||||
m.insert(b"font-face-uri", LAYOUT);
|
||||
m.insert(b"font-face", LAYOUT);
|
||||
m.insert(b"font", LAYOUT);
|
||||
m.insert(b"foreignObject", LAYOUT);
|
||||
m.insert(b"g", LAYOUT);
|
||||
m.insert(b"glyph", LAYOUT);
|
||||
m.insert(b"glyphRef", LAYOUT);
|
||||
m.insert(b"hatch", LAYOUT);
|
||||
m.insert(b"hatchpath", LAYOUT);
|
||||
m.insert(b"hkern", LAYOUT);
|
||||
m.insert(b"image", LAYOUT);
|
||||
m.insert(b"line", LAYOUT);
|
||||
m.insert(b"linearGradient", LAYOUT);
|
||||
m.insert(b"marker", LAYOUT);
|
||||
m.insert(b"mask", LAYOUT);
|
||||
m.insert(b"mesh", LAYOUT);
|
||||
m.insert(b"meshgradient", LAYOUT);
|
||||
m.insert(b"meshpatch", LAYOUT);
|
||||
m.insert(b"meshrow", LAYOUT);
|
||||
m.insert(b"metadata", LAYOUT);
|
||||
m.insert(b"missing-glyph", LAYOUT);
|
||||
m.insert(b"mpath", LAYOUT);
|
||||
m.insert(b"path", LAYOUT);
|
||||
m.insert(b"pattern", LAYOUT);
|
||||
m.insert(b"polygon", LAYOUT);
|
||||
m.insert(b"polyline", LAYOUT);
|
||||
m.insert(b"radialGradient", LAYOUT);
|
||||
m.insert(b"rect", LAYOUT);
|
||||
m.insert(b"set", LAYOUT);
|
||||
m.insert(b"solidcolor", LAYOUT);
|
||||
m.insert(b"stop", LAYOUT);
|
||||
m.insert(b"svg", LAYOUT);
|
||||
m.insert(b"switch", LAYOUT);
|
||||
m.insert(b"symbol", LAYOUT);
|
||||
m.insert(b"use", LAYOUT);
|
||||
m.insert(b"view", LAYOUT);
|
||||
m.insert(b"vkern", LAYOUT);
|
||||
|
||||
m
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_whitespace_minification_for_tag(
|
||||
ns: Namespace,
|
||||
// Use empty slice if root.
|
||||
tag_name: &[u8],
|
||||
descendant_of_pre: bool,
|
||||
) -> &'static WhitespaceMinification {
|
||||
if descendant_of_pre {
|
||||
WHITESPACE_SENSITIVE
|
||||
} else if tag_name.is_empty() {
|
||||
ROOT
|
||||
} else {
|
||||
TAG_WHITESPACE_MINIFICATION
|
||||
match ns {
|
||||
Namespace::Html => {
|
||||
if descendant_of_pre {
|
||||
WHITESPACE_SENSITIVE
|
||||
} else if tag_name.is_empty() {
|
||||
ROOT
|
||||
} else {
|
||||
HTML_TAG_WHITESPACE_MINIFICATION
|
||||
.get(tag_name)
|
||||
.unwrap_or(&DEFAULT_HTML)
|
||||
}
|
||||
}
|
||||
Namespace::Svg => SVG_TAG_WHITESPACE_MINIFICATION
|
||||
.get(tag_name)
|
||||
.unwrap_or(&DEFAULT)
|
||||
.unwrap_or(&DEFAULT_SVG),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ fn test_collapse_destroy_whole_and_trim_whitespace() {
|
|||
b"<ul> \n a<pre></pre> <pre></pre>b </ul>",
|
||||
b"<ul>a<pre></pre><pre></pre>b</ul>",
|
||||
);
|
||||
eval(
|
||||
b"<svg> <path> </path> <path> </path> </svg>",
|
||||
b"<svg><path></path><path></path></svg>",
|
||||
);
|
||||
// Tag names should be case insensitive.
|
||||
eval(b"<uL> \n a b </UL>", b"<ul>a b</ul>");
|
||||
}
|
||||
|
|
|
@ -39,7 +39,14 @@ pub fn minify(src: &[u8], cfg: &Cfg) -> Vec<u8> {
|
|||
let mut code = Code::new(src);
|
||||
let parsed = parse_content(&mut code, Namespace::Html, EMPTY_SLICE, EMPTY_SLICE);
|
||||
let mut out = Vec::with_capacity(src.len());
|
||||
minify_content(cfg, &mut out, false, EMPTY_SLICE, parsed.children);
|
||||
minify_content(
|
||||
cfg,
|
||||
&mut out,
|
||||
Namespace::Html,
|
||||
false,
|
||||
EMPTY_SLICE,
|
||||
parsed.children,
|
||||
);
|
||||
out
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::ast::{NodeData, ScriptOrStyleLang};
|
|||
use crate::cfg::Cfg;
|
||||
use crate::common::gen::codepoints::TAG_NAME_CHAR;
|
||||
use crate::common::pattern::Replacer;
|
||||
use crate::common::spec::tag::ns::Namespace;
|
||||
use crate::common::spec::tag::whitespace::{
|
||||
get_whitespace_minification_for_tag, WhitespaceMinification,
|
||||
};
|
||||
|
@ -47,6 +48,7 @@ lazy_static! {
|
|||
pub fn minify_content(
|
||||
cfg: &Cfg,
|
||||
out: &mut Vec<u8>,
|
||||
ns: Namespace,
|
||||
descendant_of_pre: bool,
|
||||
// Use empty slice if none.
|
||||
parent: &[u8],
|
||||
|
@ -56,7 +58,7 @@ pub fn minify_content(
|
|||
collapse,
|
||||
destroy_whole,
|
||||
trim,
|
||||
} = get_whitespace_minification_for_tag(parent, descendant_of_pre);
|
||||
} = get_whitespace_minification_for_tag(ns, parent, descendant_of_pre);
|
||||
|
||||
// TODO Document or fix: even though bangs/comments/etc. don't affect layout, we don't collapse/destroy-whole/trim combined text nodes across bangs/comments/etc., as that's too complex and is ambiguous about which nodes should whitespace be deleted from.
|
||||
let mut found_first_text_or_elem = false;
|
||||
|
|
|
@ -105,6 +105,11 @@ pub fn minify_element(
|
|||
minify_content(
|
||||
cfg,
|
||||
out,
|
||||
if tag_name == b"svg" {
|
||||
Namespace::Svg
|
||||
} else {
|
||||
ns
|
||||
},
|
||||
descendant_of_pre || (ns == Namespace::Html && tag_name == b"pre"),
|
||||
tag_name,
|
||||
children,
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn process_content(
|
|||
collapse,
|
||||
destroy_whole,
|
||||
trim,
|
||||
} = get_whitespace_minification_for_tag(proc.get_or_empty(parent), descendant_of_pre);
|
||||
} = get_whitespace_minification_for_tag(ns, proc.get_or_empty(parent), descendant_of_pre);
|
||||
|
||||
let handle_ws = collapse || destroy_whole || trim;
|
||||
|
||||
|
|
Loading…
Reference in New Issue