Comparing version
@@ -281,3 +281,4 @@ 'use strict'; | ||
(command === 'A' || command === 'a') && | ||
(i === 4 || i === 5) | ||
// consider combined arcs | ||
(i % 7 === 4 || i % 7 === 5) | ||
) { | ||
@@ -284,0 +285,0 @@ result += numberString; |
@@ -6,4 +6,3 @@ 'use strict'; | ||
const specificity = require('csso/lib/restructure/prepare/specificity'); | ||
const { selectAll, is } = require('css-select'); | ||
const svgoCssSelectAdapter = require('./svgo/css-select-adapter.js'); | ||
const { visit, matches } = require('./xast.js'); | ||
const { compareSpecificity } = require('./css-tools.js'); | ||
@@ -16,7 +15,2 @@ const { | ||
const cssSelectOptions = { | ||
xmlMode: true, | ||
adapter: svgoCssSelectAdapter, | ||
}; | ||
const parseRule = (ruleNode, dynamic) => { | ||
@@ -81,3 +75,3 @@ let selectors; | ||
const computeOwnStyle = (node, stylesheet) => { | ||
const computeOwnStyle = (stylesheet, node) => { | ||
const computedStyle = {}; | ||
@@ -96,3 +90,3 @@ const importantStyles = new Map(); | ||
for (const { selectors, declarations, dynamic } of stylesheet) { | ||
if (is(node, selectors, cssSelectOptions)) { | ||
if (matches(node, selectors)) { | ||
for (const { name, value, important } of declarations) { | ||
@@ -139,29 +133,27 @@ const computed = computedStyle[name]; | ||
const computeStyle = (node) => { | ||
// find root | ||
let root = node; | ||
while (root.parentNode) { | ||
root = root.parentNode; | ||
} | ||
// find all styles | ||
const styleNodes = selectAll('style', root, cssSelectOptions); | ||
// parse all styles | ||
const collectStylesheet = (root) => { | ||
const stylesheet = []; | ||
for (const styleNode of styleNodes) { | ||
const dynamic = | ||
styleNode.attributes.media != null && | ||
styleNode.attributes.media !== 'all'; | ||
if ( | ||
styleNode.attributes.type == null || | ||
styleNode.attributes.type === '' || | ||
styleNode.attributes.type === 'text/css' | ||
) { | ||
const children = styleNode.children; | ||
for (const child of children) { | ||
if (child.type === 'text' || child.type === 'cdata') { | ||
stylesheet.push(...parseStylesheet(child.value, dynamic)); | ||
// find and parse all styles | ||
visit(root, { | ||
element: { | ||
enter: (node) => { | ||
if (node.name === 'style') { | ||
const dynamic = | ||
node.attributes.media != null && node.attributes.media !== 'all'; | ||
if ( | ||
node.attributes.type == null || | ||
node.attributes.type === '' || | ||
node.attributes.type === 'text/css' | ||
) { | ||
const children = node.children; | ||
for (const child of children) { | ||
if (child.type === 'text' || child.type === 'cdata') { | ||
stylesheet.push(...parseStylesheet(child.value, dynamic)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
}, | ||
}); | ||
// sort by selectors specificity | ||
@@ -171,8 +163,12 @@ stable.inplace(stylesheet, (a, b) => | ||
); | ||
return stylesheet; | ||
}; | ||
exports.collectStylesheet = collectStylesheet; | ||
const computeStyle = (stylesheet, node) => { | ||
// collect inherited styles | ||
const computedStyles = computeOwnStyle(node, stylesheet); | ||
const computedStyles = computeOwnStyle(stylesheet, node); | ||
let parent = node; | ||
while (parent.parentNode && parent.parentNode.type !== 'root') { | ||
const inheritedStyles = computeOwnStyle(parent.parentNode, stylesheet); | ||
const inheritedStyles = computeOwnStyle(stylesheet, parent.parentNode); | ||
for (const [name, computed] of Object.entries(inheritedStyles)) { | ||
@@ -179,0 +175,0 @@ if ( |
@@ -10,3 +10,3 @@ 'use strict'; | ||
const js2svg = require('./svgo/js2svg.js'); | ||
const invokePlugins = require('./svgo/plugins.js'); | ||
const { invokePlugins } = require('./svgo/plugins.js'); | ||
const JSAPI = require('./svgo/jsAPI.js'); | ||
@@ -46,6 +46,8 @@ const { encodeSVGDatauri } = require('./svgo/tools.js'); | ||
} | ||
const resolvedPlugins = plugins.map((plugin) => | ||
resolvePluginConfig(plugin, config) | ||
); | ||
svgjs = invokePlugins(svgjs, info, resolvedPlugins); | ||
const resolvedPlugins = plugins.map(resolvePluginConfig); | ||
const globalOverrides = {}; | ||
if (config.floatPrecision != null) { | ||
globalOverrides.floatPrecision = config.floatPrecision; | ||
} | ||
svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides); | ||
svgjs = js2svg(svgjs, config.js2svg); | ||
@@ -52,0 +54,0 @@ if (svgjs.error) { |
@@ -5,3 +5,3 @@ 'use strict'; | ||
const PATH = require('path'); | ||
const chalk = require('chalk'); | ||
const { green } = require('colorette'); | ||
const { loadConfig, optimize } = require('../svgo-node.js'); | ||
@@ -437,3 +437,3 @@ const pluginsMap = require('../../plugins/plugins.js'); | ||
(profitPercents < 0 ? ' + ' : ' - ') + | ||
chalk.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') + | ||
green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') + | ||
' = ' + | ||
@@ -490,3 +490,3 @@ Math.round((outBytes / 1024) * 1000) / 1000 + | ||
.sort(([a], [b]) => a.localeCompare(b)) | ||
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`) | ||
.map(([name, plugin]) => ` [ ${green(name)} ] ${plugin.description}`) | ||
.join('\n'); | ||
@@ -493,0 +493,0 @@ console.log('Currently available plugins:\n' + list); |
@@ -61,2 +61,20 @@ 'use strict'; | ||
const extendDefaultPlugins = (plugins) => { | ||
console.warn( | ||
'\n"extendDefaultPlugins" utility is deprecated.\n' + | ||
'Use "preset-default" plugin with overrides instead.\n' + | ||
'For example:\n' + | ||
`{\n` + | ||
` name: 'preset-default',\n` + | ||
` params: {\n` + | ||
` overrides: {\n` + | ||
` // customize plugin options\n` + | ||
` convertShapeToPath: {\n` + | ||
` convertArcs: true\n` + | ||
` },\n` + | ||
` // disable plugins\n` + | ||
` convertPathData: false\n` + | ||
` }\n` + | ||
` }\n` + | ||
`}\n` | ||
); | ||
const extendedPlugins = pluginsOrder.map((name) => ({ | ||
@@ -67,3 +85,3 @@ name, | ||
for (const plugin of plugins) { | ||
const resolvedPlugin = resolvePluginConfig(plugin, {}); | ||
const resolvedPlugin = resolvePluginConfig(plugin); | ||
const index = pluginsOrder.indexOf(resolvedPlugin.name); | ||
@@ -80,7 +98,4 @@ if (index === -1) { | ||
const resolvePluginConfig = (plugin, config) => { | ||
const resolvePluginConfig = (plugin) => { | ||
let configParams = {}; | ||
if ('floatPrecision' in config) { | ||
configParams.floatPrecision = config.floatPrecision; | ||
} | ||
if (typeof plugin === 'string') { | ||
@@ -87,0 +102,0 @@ // resolve builtin plugin specified as string |
@@ -335,3 +335,3 @@ 'use strict'; | ||
// newly added class attribute | ||
this.class.hasClass(); | ||
this.class.addClassValueHandler(); | ||
} | ||
@@ -341,3 +341,3 @@ | ||
// newly added style attribute | ||
this.style.hasStyle(); | ||
this.style.addStyleValueHandler(); | ||
} | ||
@@ -344,0 +344,0 @@ |
@@ -10,43 +10,36 @@ 'use strict'; | ||
* | ||
* @param {Object} data input data | ||
* @param {Object} ast input ast | ||
* @param {Object} info extra information | ||
* @param {Array} plugins plugins object from config | ||
* @return {Object} output data | ||
* @return {Object} output ast | ||
*/ | ||
module.exports = function (data, info, plugins) { | ||
// Try to group sequential elements of plugins array | ||
// to optimize ast traversing | ||
const groups = []; | ||
let prev; | ||
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => { | ||
for (const plugin of plugins) { | ||
if (prev && plugin.type == prev[0].type) { | ||
prev.push(plugin); | ||
} else { | ||
prev = [plugin]; | ||
groups.push(prev); | ||
const override = overrides == null ? null : overrides[plugin.name]; | ||
if (override === false) { | ||
continue; | ||
} | ||
} | ||
for (const group of groups) { | ||
switch (group[0].type) { | ||
case 'perItem': | ||
data = perItem(data, info, group); | ||
break; | ||
case 'perItemReverse': | ||
data = perItem(data, info, group, true); | ||
break; | ||
case 'full': | ||
data = full(data, info, group); | ||
break; | ||
case 'visitor': | ||
for (const plugin of group) { | ||
if (plugin.active) { | ||
const visitor = plugin.fn(data, plugin.params, info); | ||
visit(data, visitor); | ||
} | ||
} | ||
break; | ||
const params = { ...plugin.params, ...globalOverrides, ...override }; | ||
if (plugin.type === 'perItem') { | ||
ast = perItem(ast, info, plugin, params); | ||
} | ||
if (plugin.type === 'perItemReverse') { | ||
ast = perItem(ast, info, plugin, params, true); | ||
} | ||
if (plugin.type === 'full') { | ||
if (plugin.active) { | ||
ast = plugin.fn(ast, params, info); | ||
} | ||
} | ||
if (plugin.type === 'visitor') { | ||
if (plugin.active) { | ||
const visitor = plugin.fn(ast, params, info); | ||
visit(ast, visitor); | ||
} | ||
} | ||
} | ||
return data; | ||
return ast; | ||
}; | ||
exports.invokePlugins = invokePlugins; | ||
@@ -62,3 +55,3 @@ /** | ||
*/ | ||
function perItem(data, info, plugins, reverse) { | ||
function perItem(data, info, plugin, params, reverse) { | ||
function monkeys(items) { | ||
@@ -70,14 +63,7 @@ items.children = items.children.filter(function (item) { | ||
} | ||
// main filter | ||
var filter = true; | ||
for (var i = 0; filter && i < plugins.length; i++) { | ||
var plugin = plugins[i]; | ||
if (plugin.active && plugin.fn(item, plugin.params, info) === false) { | ||
filter = false; | ||
} | ||
let kept = true; | ||
if (plugin.active) { | ||
kept = plugin.fn(item, params, info) !== false; | ||
} | ||
// direct pass | ||
@@ -87,28 +73,23 @@ if (!reverse && item.children) { | ||
} | ||
return filter; | ||
return kept; | ||
}); | ||
return items; | ||
} | ||
return monkeys(data); | ||
} | ||
/** | ||
* "Full" plugins. | ||
* | ||
* @param {Object} data input data | ||
* @param {Object} info extra information | ||
* @param {Array} plugins plugins list to process | ||
* @return {Object} output data | ||
*/ | ||
function full(data, info, plugins) { | ||
plugins.forEach(function (plugin) { | ||
if (plugin.active) { | ||
data = plugin.fn(data, plugin.params, info); | ||
} | ||
}); | ||
return data; | ||
} | ||
const createPreset = ({ name, plugins }) => { | ||
return { | ||
name, | ||
type: 'full', | ||
fn: (ast, params, info) => { | ||
const { floatPrecision, overrides } = params; | ||
const globalOverrides = {}; | ||
if (floatPrecision != null) { | ||
globalOverrides.floatPrecision = floatPrecision; | ||
} | ||
return invokePlugins(ast, info, plugins, overrides, globalOverrides); | ||
}, | ||
}; | ||
}; | ||
exports.createPreset = createPreset; |
@@ -55,12 +55,2 @@ 'use strict'; | ||
/** | ||
* @param {any[]} a | ||
* @param {any[]} b | ||
*/ | ||
exports.intersectArrays = function (a, b) { | ||
return a.filter(function (n) { | ||
return b.indexOf(n) > -1; | ||
}); | ||
}; | ||
/** | ||
* Convert a row of numbers to an optimized string view. | ||
@@ -67,0 +57,0 @@ * |
@@ -55,6 +55,6 @@ 'use strict'; | ||
const visit = (node, visitor) => { | ||
const visit = (node, visitor, parentNode = null) => { | ||
const callbacks = visitor[node.type]; | ||
if (callbacks && callbacks.enter) { | ||
callbacks.enter(node); | ||
callbacks.enter(node, parentNode); | ||
} | ||
@@ -65,3 +65,3 @@ // visit root children | ||
for (const child of node.children) { | ||
visit(child, visitor); | ||
visit(child, visitor, node); | ||
} | ||
@@ -71,5 +71,5 @@ } | ||
if (node.type === 'element') { | ||
if (node.parentNode.children.includes(node)) { | ||
if (parentNode.children.includes(node)) { | ||
for (const child of node.children) { | ||
visit(child, visitor); | ||
visit(child, visitor, node); | ||
} | ||
@@ -79,3 +79,3 @@ } | ||
if (callbacks && callbacks.exit) { | ||
callbacks.exit(node); | ||
callbacks.exit(node, parentNode); | ||
} | ||
@@ -85,4 +85,3 @@ }; | ||
const detachNodeFromParent = (node) => { | ||
const parentNode = node.parentNode; | ||
const detachNodeFromParent = (node, parentNode) => { | ||
// avoid splice to not break for loops | ||
@@ -89,0 +88,0 @@ parentNode.children = parentNode.children.filter((child) => child !== node); |
{ | ||
"name": "svgo", | ||
"version": "2.3.1", | ||
"version": "2.4.0", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -49,7 +49,8 @@ "keywords": [ | ||
"plugins", | ||
"dist" | ||
"dist", | ||
"!**/**.test.js" | ||
], | ||
"scripts": { | ||
"test": "c8 --reporter=html --reporter=text mocha \"test/*/_index.js\" \"**/*.test.js\" --ignore=\"node_modules/**\"", | ||
"lint": "eslint --ignore-path .gitignore . && prettier --list-different \"**/*.js\" --ignore-path .gitignore", | ||
"test": "jest --coverage", | ||
"lint": "eslint --ignore-path .gitignore . && prettier --check \"**/*.js\" --ignore-path .gitignore", | ||
"fix": "eslint --ignore-path .gitignore --fix . && prettier --write \"**/*.js\" --ignore-path .gitignore", | ||
@@ -86,7 +87,6 @@ "typecheck": "tsc", | ||
"files": [ | ||
"test/**/*.js", | ||
"**/*.test.js" | ||
], | ||
"env": { | ||
"mocha": true | ||
"jest": true | ||
} | ||
@@ -98,3 +98,3 @@ } | ||
"@trysound/sax": "0.1.1", | ||
"chalk": "^4.1.0", | ||
"colorette": "^1.2.2", | ||
"commander": "^7.1.0", | ||
@@ -110,8 +110,6 @@ "css-select": "^4.1.3", | ||
"@rollup/plugin-node-resolve": "^11.2.0", | ||
"@types/mocha": "^8.2.2", | ||
"c8": "^7.6.0", | ||
"chai": "^4.3.4", | ||
"@types/jest": "^27.0.0", | ||
"del": "^6.0.0", | ||
"eslint": "^7.22.0", | ||
"mocha": "^8.3.2", | ||
"jest": "^27.0.6", | ||
"mock-stdin": "^1.0.0", | ||
@@ -118,0 +116,0 @@ "node-fetch": "^2.6.1", |
@@ -326,2 +326,7 @@ 'use strict'; | ||
if (command === 'z' || command === 'Z') { | ||
cursor[0] = start[0]; | ||
cursor[1] = start[1]; | ||
} | ||
pathItem.instruction = command; | ||
@@ -328,0 +333,0 @@ pathItem.data = args; |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'addAttributesToSVGElement'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.name = 'addClassesToSVGElement'; | ||
exports.type = 'full'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
exports.name = 'cleanupAttrs'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = | ||
'cleanups attributes from newlines, trailing and repeating spaces'; | ||
exports.params = { | ||
newlines: true, | ||
trim: true, | ||
spaces: true, | ||
}; | ||
const regNewlinesNeedSpace = /(\S)\r?\n(\S)/g; | ||
const regNewlines = /\r?\n/g; | ||
const regSpaces = /\s{2,}/g; | ||
var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g, | ||
regNewlines = /\r?\n/g, | ||
regSpaces = /\s{2,}/g; | ||
/** | ||
* Cleanup attributes values from newlines, trailing and repeating spaces. | ||
* | ||
* @param {Object} item current iteration item | ||
* @param {Object} params plugin params | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item, params) { | ||
if (item.type === 'element') { | ||
for (const name of Object.keys(item.attributes)) { | ||
if (params.newlines) { | ||
// new line which requires a space instead of themselve | ||
item.attributes[name] = item.attributes[name].replace( | ||
regNewlinesNeedSpace, | ||
(match, p1, p2) => p1 + ' ' + p2 | ||
); | ||
// simple new line | ||
item.attributes[name] = item.attributes[name].replace(regNewlines, ''); | ||
} | ||
if (params.trim) { | ||
item.attributes[name] = item.attributes[name].trim(); | ||
} | ||
if (params.spaces) { | ||
item.attributes[name] = item.attributes[name].replace(regSpaces, ' '); | ||
} | ||
} | ||
} | ||
exports.fn = (root, params) => { | ||
const { newlines = true, trim = true, spaces = true } = params; | ||
return { | ||
element: { | ||
enter: (node) => { | ||
for (const name of Object.keys(node.attributes)) { | ||
if (newlines) { | ||
// new line which requires a space instead of themselve | ||
node.attributes[name] = node.attributes[name].replace( | ||
regNewlinesNeedSpace, | ||
(match, p1, p2) => p1 + ' ' + p2 | ||
); | ||
// simple new line | ||
node.attributes[name] = node.attributes[name].replace( | ||
regNewlines, | ||
'' | ||
); | ||
} | ||
if (trim) { | ||
node.attributes[name] = node.attributes[name].trim(); | ||
} | ||
if (spaces) { | ||
node.attributes[name] = node.attributes[name].replace( | ||
regSpaces, | ||
' ' | ||
); | ||
} | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'cleanupEnableBackground'; | ||
exports.type = 'full'; | ||
@@ -7,0 +9,0 @@ |
@@ -6,2 +6,4 @@ 'use strict'; | ||
exports.name = 'cleanupIDs'; | ||
exports.type = 'full'; | ||
@@ -8,0 +10,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'cleanupListOfValues'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.name = 'cleanupNumericValues'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'collapseGroups'; | ||
exports.type = 'perItemReverse'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.name = 'convertColors'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
exports.name = 'convertEllipseToCircle'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'converts non-eccentric <ellipse>s to <circle>s'; | ||
@@ -14,24 +13,26 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Taylor Hunt | ||
*/ | ||
exports.fn = function (item) { | ||
if (item.isElem('ellipse')) { | ||
const rx = item.attributes.rx || 0; | ||
const ry = item.attributes.ry || 0; | ||
if ( | ||
rx === ry || | ||
rx === 'auto' || | ||
ry === 'auto' // SVG2 | ||
) { | ||
var radius = rx !== 'auto' ? rx : ry; | ||
item.renameElem('circle'); | ||
delete item.attributes.rx; | ||
delete item.attributes.ry; | ||
item.attributes.r = radius; | ||
} | ||
} | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node) => { | ||
if (node.name === 'ellipse') { | ||
const rx = node.attributes.rx || 0; | ||
const ry = node.attributes.ry || 0; | ||
if ( | ||
rx === ry || | ||
rx === 'auto' || | ||
ry === 'auto' // SVG2 | ||
) { | ||
node.name = 'circle'; | ||
const radius = rx === 'auto' ? ry : rx; | ||
delete node.attributes.rx; | ||
delete node.attributes.ry; | ||
node.attributes.r = radius; | ||
} | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
const { computeStyle } = require('../lib/style.js'); | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { pathElems } = require('./_collections.js'); | ||
@@ -9,2 +9,3 @@ const { path2js, js2path } = require('./_path.js'); | ||
exports.name = 'convertPathData'; | ||
exports.type = 'visitor'; | ||
@@ -59,2 +60,3 @@ exports.active = true; | ||
exports.fn = (root, params) => { | ||
const stylesheet = collectStylesheet(root); | ||
return { | ||
@@ -64,3 +66,3 @@ element: { | ||
if (pathElems.includes(node.name) && node.attributes.d != null) { | ||
const computedStyle = computeStyle(node); | ||
const computedStyle = computeStyle(stylesheet, node); | ||
precision = params.floatPrecision; | ||
@@ -67,0 +69,0 @@ error = |
'use strict'; | ||
const { stringifyPathData } = require('../lib/path.js'); | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.type = 'perItem'; | ||
exports.name = 'convertShapeToPath'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'converts basic shapes to more compact path form'; | ||
exports.params = { | ||
convertArcs: false, | ||
floatPrecision: null, | ||
}; | ||
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; | ||
@@ -25,122 +20,133 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @param {Object} params plugin params | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Lev Solntsev | ||
*/ | ||
exports.fn = function (item, params) { | ||
const precision = params ? params.floatPrecision : null; | ||
const convertArcs = params && params.convertArcs; | ||
exports.fn = (root, params) => { | ||
const { convertArcs = false, floatPrecision: precision = null } = params; | ||
if ( | ||
item.isElem('rect') && | ||
item.attributes.width != null && | ||
item.attributes.height != null && | ||
item.attributes.rx == null && | ||
item.attributes.ry == null | ||
) { | ||
const x = Number(item.attributes.x || '0'); | ||
const y = Number(item.attributes.y || '0'); | ||
const width = Number(item.attributes.width); | ||
const height = Number(item.attributes.height); | ||
// Values like '100%' compute to NaN, thus running after | ||
// cleanupNumericValues when 'px' units has already been removed. | ||
// TODO: Calculate sizes from % and non-px units if possible. | ||
if (isNaN(x - y + width - height)) return; | ||
const pathData = [ | ||
{ command: 'M', args: [x, y] }, | ||
{ command: 'H', args: [x + width] }, | ||
{ command: 'V', args: [y + height] }, | ||
{ command: 'H', args: [x] }, | ||
{ command: 'z', args: [] }, | ||
]; | ||
item.attributes.d = stringifyPathData({ pathData, precision }); | ||
item.renameElem('path'); | ||
delete item.attributes.x; | ||
delete item.attributes.y; | ||
delete item.attributes.width; | ||
delete item.attributes.height; | ||
} | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
// convert rect to path | ||
if ( | ||
node.name === 'rect' && | ||
node.attributes.width != null && | ||
node.attributes.height != null && | ||
node.attributes.rx == null && | ||
node.attributes.ry == null | ||
) { | ||
const x = Number(node.attributes.x || '0'); | ||
const y = Number(node.attributes.y || '0'); | ||
const width = Number(node.attributes.width); | ||
const height = Number(node.attributes.height); | ||
// Values like '100%' compute to NaN, thus running after | ||
// cleanupNumericValues when 'px' units has already been removed. | ||
// TODO: Calculate sizes from % and non-px units if possible. | ||
if (Number.isNaN(x - y + width - height)) return; | ||
const pathData = [ | ||
{ command: 'M', args: [x, y] }, | ||
{ command: 'H', args: [x + width] }, | ||
{ command: 'V', args: [y + height] }, | ||
{ command: 'H', args: [x] }, | ||
{ command: 'z', args: [] }, | ||
]; | ||
node.name = 'path'; | ||
node.attributes.d = stringifyPathData({ pathData, precision }); | ||
delete node.attributes.x; | ||
delete node.attributes.y; | ||
delete node.attributes.width; | ||
delete node.attributes.height; | ||
} | ||
if (item.isElem('line')) { | ||
const x1 = Number(item.attributes.x1 || '0'); | ||
const y1 = Number(item.attributes.y1 || '0'); | ||
const x2 = Number(item.attributes.x2 || '0'); | ||
const y2 = Number(item.attributes.y2 || '0'); | ||
if (isNaN(x1 - y1 + x2 - y2)) return; | ||
const pathData = [ | ||
{ command: 'M', args: [x1, y1] }, | ||
{ command: 'L', args: [x2, y2] }, | ||
]; | ||
item.attributes.d = stringifyPathData({ pathData, precision }); | ||
item.renameElem('path'); | ||
delete item.attributes.x1; | ||
delete item.attributes.y1; | ||
delete item.attributes.x2; | ||
delete item.attributes.y2; | ||
} | ||
// convert line to path | ||
if (node.name === 'line') { | ||
const x1 = Number(node.attributes.x1 || '0'); | ||
const y1 = Number(node.attributes.y1 || '0'); | ||
const x2 = Number(node.attributes.x2 || '0'); | ||
const y2 = Number(node.attributes.y2 || '0'); | ||
if (Number.isNaN(x1 - y1 + x2 - y2)) return; | ||
const pathData = [ | ||
{ command: 'M', args: [x1, y1] }, | ||
{ command: 'L', args: [x2, y2] }, | ||
]; | ||
node.name = 'path'; | ||
node.attributes.d = stringifyPathData({ pathData, precision }); | ||
delete node.attributes.x1; | ||
delete node.attributes.y1; | ||
delete node.attributes.x2; | ||
delete node.attributes.y2; | ||
} | ||
if ( | ||
(item.isElem('polyline') || item.isElem('polygon')) && | ||
item.attributes.points != null | ||
) { | ||
const coords = (item.attributes.points.match(regNumber) || []).map(Number); | ||
if (coords.length < 4) return false; | ||
const pathData = []; | ||
for (let i = 0; i < coords.length; i += 2) { | ||
pathData.push({ | ||
command: i === 0 ? 'M' : 'L', | ||
args: coords.slice(i, i + 2), | ||
}); | ||
} | ||
if (item.isElem('polygon')) { | ||
pathData.push({ command: 'z', args: [] }); | ||
} | ||
item.attributes.d = stringifyPathData({ pathData, precision }); | ||
item.renameElem('path'); | ||
delete item.attributes.points; | ||
} | ||
// convert polyline and polygon to path | ||
if ( | ||
(node.name === 'polyline' || node.name === 'polygon') && | ||
node.attributes.points != null | ||
) { | ||
const coords = (node.attributes.points.match(regNumber) || []).map( | ||
Number | ||
); | ||
if (coords.length < 4) { | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
} | ||
const pathData = []; | ||
for (let i = 0; i < coords.length; i += 2) { | ||
pathData.push({ | ||
command: i === 0 ? 'M' : 'L', | ||
args: coords.slice(i, i + 2), | ||
}); | ||
} | ||
if (node.name === 'polygon') { | ||
pathData.push({ command: 'z', args: [] }); | ||
} | ||
node.name = 'path'; | ||
node.attributes.d = stringifyPathData({ pathData, precision }); | ||
delete node.attributes.points; | ||
} | ||
if (item.isElem('circle') && convertArcs) { | ||
const cx = Number(item.attributes.cx || '0'); | ||
const cy = Number(item.attributes.cy || '0'); | ||
const r = Number(item.attributes.r || '0'); | ||
if (isNaN(cx - cy + r)) { | ||
return; | ||
} | ||
const pathData = [ | ||
{ command: 'M', args: [cx, cy - r] }, | ||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] }, | ||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] }, | ||
{ command: 'z', args: [] }, | ||
]; | ||
item.attributes.d = stringifyPathData({ pathData, precision }); | ||
item.renameElem('path'); | ||
delete item.attributes.cx; | ||
delete item.attributes.cy; | ||
delete item.attributes.r; | ||
} | ||
// optionally convert circle | ||
if (node.name === 'circle' && convertArcs) { | ||
const cx = Number(node.attributes.cx || '0'); | ||
const cy = Number(node.attributes.cy || '0'); | ||
const r = Number(node.attributes.r || '0'); | ||
if (Number.isNaN(cx - cy + r)) { | ||
return; | ||
} | ||
const pathData = [ | ||
{ command: 'M', args: [cx, cy - r] }, | ||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] }, | ||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] }, | ||
{ command: 'z', args: [] }, | ||
]; | ||
node.name = 'path'; | ||
node.attributes.d = stringifyPathData({ pathData, precision }); | ||
delete node.attributes.cx; | ||
delete node.attributes.cy; | ||
delete node.attributes.r; | ||
} | ||
if (item.isElem('ellipse') && convertArcs) { | ||
const ecx = Number(item.attributes.cx || '0'); | ||
const ecy = Number(item.attributes.cy || '0'); | ||
const rx = Number(item.attributes.rx || '0'); | ||
const ry = Number(item.attributes.ry || '0'); | ||
if (isNaN(ecx - ecy + rx - ry)) { | ||
return; | ||
} | ||
const pathData = [ | ||
{ command: 'M', args: [ecx, ecy - ry] }, | ||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] }, | ||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] }, | ||
{ command: 'z', args: [] }, | ||
]; | ||
item.attributes.d = stringifyPathData({ pathData, precision }); | ||
item.renameElem('path'); | ||
delete item.attributes.cx; | ||
delete item.attributes.cy; | ||
delete item.attributes.rx; | ||
delete item.attributes.ry; | ||
} | ||
// optionally covert ellipse | ||
if (node.name === 'ellipse' && convertArcs) { | ||
const ecx = Number(node.attributes.cx || '0'); | ||
const ecy = Number(node.attributes.cy || '0'); | ||
const rx = Number(node.attributes.rx || '0'); | ||
const ry = Number(node.attributes.ry || '0'); | ||
if (Number.isNaN(ecx - ecy + rx - ry)) { | ||
return; | ||
} | ||
const pathData = [ | ||
{ command: 'M', args: [ecx, ecy - ry] }, | ||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] }, | ||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] }, | ||
{ command: 'z', args: [] }, | ||
]; | ||
node.name = 'path'; | ||
node.attributes.d = stringifyPathData({ pathData, precision }); | ||
delete node.attributes.cx; | ||
delete node.attributes.cy; | ||
delete node.attributes.rx; | ||
delete node.attributes.ry; | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.name = 'convertStyleToAttrs'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.name = 'convertTransform'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
@@ -7,2 +7,4 @@ 'use strict'; | ||
exports.name = 'inlineStyles'; | ||
exports.type = 'full'; | ||
@@ -9,0 +11,0 @@ |
'use strict'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
const { computeStyle } = require('../lib/style.js'); | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { path2js, js2path, intersects } = require('./_path.js'); | ||
exports.name = 'mergePaths'; | ||
exports.type = 'visitor'; | ||
@@ -25,2 +26,3 @@ exports.active = true; | ||
} = params; | ||
const stylesheet = collectStylesheet(root); | ||
@@ -57,3 +59,3 @@ return { | ||
// preserve paths with markers | ||
const computedStyle = computeStyle(child); | ||
const computedStyle = computeStyle(stylesheet, child); | ||
if ( | ||
@@ -92,3 +94,3 @@ computedStyle['marker-start'] || | ||
}); | ||
detachNodeFromParent(child); | ||
detachNodeFromParent(child, node); | ||
continue; | ||
@@ -95,0 +97,0 @@ } |
@@ -6,2 +6,3 @@ 'use strict'; | ||
exports.name = 'mergeStyles'; | ||
exports.type = 'visitor'; | ||
@@ -21,3 +22,3 @@ exports.active = true; | ||
const enterElement = (node) => { | ||
const enterElement = (node, parentNode) => { | ||
// collect style elements | ||
@@ -56,3 +57,3 @@ if (node.name !== 'style') { | ||
if (css.trim().length === 0) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -73,3 +74,3 @@ } | ||
} else { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
firstStyleElement.children = [ | ||
@@ -76,0 +77,0 @@ new JSAPI( |
@@ -6,2 +6,4 @@ 'use strict'; | ||
exports.name = 'minifyStyles'; | ||
exports.type = 'full'; | ||
@@ -8,0 +10,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'moveElemsAttrsToGroup'; | ||
exports.type = 'perItemReverse'; | ||
@@ -7,0 +9,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'moveGroupAttrsToElems'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
// builtin presets | ||
exports['preset-default'] = require('./preset-default.js'); | ||
// builtin plugins | ||
exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js'); | ||
@@ -4,0 +8,0 @@ exports.addClassesToSVGElement = require('./addClassesToSVGElement.js'); |
'use strict'; | ||
exports.name = 'prefixIds'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.name = 'removeAttributesBySelector'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'removeAttrs'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeComments'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'removes comments'; | ||
@@ -16,11 +17,14 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item) { | ||
if (item.type === 'comment' && item.value.charAt(0) !== '!') { | ||
return false; | ||
} | ||
exports.fn = () => { | ||
return { | ||
comment: { | ||
enter: (node, parentNode) => { | ||
if (node.value.charAt(0) !== '!') { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeDesc'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.params = { | ||
removeAny: true, | ||
}; | ||
exports.description = 'removes <desc>'; | ||
var standardDescs = /^(Created with|Created using)/; | ||
const standardDescs = /^(Created with|Created using)/; | ||
@@ -22,17 +19,22 @@ /** | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Daniel Wabyick | ||
*/ | ||
exports.fn = function (item, params) { | ||
return ( | ||
!item.isElem('desc') || | ||
!( | ||
params.removeAny || | ||
item.children.length === 0 || | ||
(item.children[0].type === 'text' && | ||
standardDescs.test(item.children[0].value)) | ||
) | ||
); | ||
exports.fn = (root, params) => { | ||
const { removeAny = true } = params; | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if (node.name === 'desc') { | ||
if ( | ||
removeAny || | ||
node.children.length === 0 || | ||
(node.children[0].type === 'text' && | ||
standardDescs.test(node.children[0].value)) | ||
) { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.name = 'removeDimensions'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeDoctype'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'removes doctype declaration'; | ||
@@ -29,11 +30,12 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item) { | ||
if (item.type === 'doctype') { | ||
return false; | ||
} | ||
exports.fn = () => { | ||
return { | ||
doctype: { | ||
enter: (node, parentNode) => { | ||
detachNodeFromParent(node, parentNode); | ||
}, | ||
}, | ||
}; | ||
}; |
@@ -6,2 +6,4 @@ 'use strict'; | ||
exports.name = 'removeEditorsNSData'; | ||
exports.type = 'perItem'; | ||
@@ -8,0 +10,0 @@ |
'use strict'; | ||
exports.name = 'removeElementsByAttr'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'removeEmptyAttrs'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'removeEmptyContainers'; | ||
exports.type = 'perItemReverse'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeEmptyText'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'removes empty <text> elements'; | ||
exports.params = { | ||
text: true, | ||
tspan: true, | ||
tref: true, | ||
}; | ||
/** | ||
@@ -30,29 +25,28 @@ * Remove empty Text elements. | ||
* | ||
* @param {Object} item current iteration item | ||
* @param {Object} params plugin params | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item, params) { | ||
if (item.type === 'element') { | ||
// Remove empty text element | ||
if (params.text && item.name === 'text' && item.children.length === 0) { | ||
return false; | ||
} | ||
// Remove empty tspan element | ||
if (params.tspan && item.name === 'tspan' && item.children.length === 0) { | ||
return false; | ||
} | ||
// Remove tref with empty xlink:href attribute | ||
if ( | ||
params.tref && | ||
item.name === 'tref' && | ||
item.attributes['xlink:href'] == null | ||
) { | ||
return false; | ||
} | ||
} | ||
exports.fn = (root, params) => { | ||
const { text = true, tspan = true, tref = true } = params; | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
// Remove empty text element | ||
if (text && node.name === 'text' && node.children.length === 0) { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
// Remove empty tspan element | ||
if (tspan && node.name === 'tspan' && node.children.length === 0) { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
// Remove tref with empty xlink:href attribute | ||
if ( | ||
tref && | ||
node.name === 'tref' && | ||
node.attributes['xlink:href'] == null | ||
) { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
@@ -8,5 +8,6 @@ 'use strict'; | ||
} = require('../lib/xast.js'); | ||
const { computeStyle } = require('../lib/style.js'); | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { parsePathData } = require('../lib/path.js'); | ||
exports.name = 'removeHiddenElems'; | ||
exports.type = 'visitor'; | ||
@@ -53,8 +54,10 @@ exports.active = true; | ||
} = params; | ||
const stylesheet = collectStylesheet(root); | ||
return { | ||
element: { | ||
enter: (node) => { | ||
enter: (node, parentNode) => { | ||
// Removes hidden elements | ||
// https://www.w3schools.com/cssref/pr_class_visibility.asp | ||
const computedStyle = computeStyle(node); | ||
const computedStyle = computeStyle(stylesheet, node); | ||
if ( | ||
@@ -68,3 +71,3 @@ isHidden && | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -86,3 +89,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -102,3 +105,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -119,3 +122,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -136,3 +139,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -153,3 +156,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -170,3 +173,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -188,3 +191,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -204,3 +207,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -220,3 +223,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -236,3 +239,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -252,3 +255,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -264,3 +267,3 @@ } | ||
if (node.attributes.d == null) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -270,3 +273,3 @@ } | ||
if (pathData.length === 0) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -280,3 +283,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -297,3 +300,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -312,3 +315,3 @@ } | ||
) { | ||
detachNodeFromParent(node); | ||
detachNodeFromParent(node, parentNode); | ||
return; | ||
@@ -315,0 +318,0 @@ } |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeMetadata'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'removes <metadata>'; | ||
@@ -14,9 +15,14 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item) { | ||
return !item.isElem('metadata'); | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if (node.name === 'metadata') { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.name = 'removeNonInheritableGroupAttrs'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.name = 'removeOffCanvasPaths'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeRasterImages'; | ||
exports.type = 'visitor'; | ||
exports.active = false; | ||
exports.description = 'removes raster images (disabled by default)'; | ||
@@ -14,16 +15,18 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item) { | ||
if ( | ||
item.type === 'element' && | ||
item.name === 'image' && | ||
item.attributes['xlink:href'] != null && | ||
/(\.|image\/)(jpg|png|gif)/.test(item.attributes['xlink:href']) | ||
) { | ||
return false; | ||
} | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if ( | ||
node.name === 'image' && | ||
node.attributes['xlink:href'] != null && | ||
/(\.|image\/)(jpg|png|gif)/.test(node.attributes['xlink:href']) | ||
) { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeScriptElement'; | ||
exports.type = 'visitor'; | ||
exports.active = false; | ||
exports.description = 'removes <script> elements (disabled by default)'; | ||
@@ -14,9 +15,15 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Patrick Klingemann | ||
*/ | ||
exports.fn = function (item) { | ||
return !item.isElem('script'); | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if (node.name === 'script') { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeStyleElement'; | ||
exports.type = 'visitor'; | ||
exports.active = false; | ||
exports.description = 'removes <style> element (disabled by default)'; | ||
@@ -14,9 +15,14 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Betsy Dupuis | ||
*/ | ||
exports.fn = function (item) { | ||
return !item.isElem('style'); | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if (node.name === 'style') { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeTitle'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'removes <title>'; | ||
@@ -14,9 +15,14 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Igor Kalashnikov | ||
*/ | ||
exports.fn = function (item) { | ||
return !item.isElem('title'); | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if (node.name === 'title') { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'removeUnknownsAndDefaults'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
@@ -6,2 +6,4 @@ 'use strict'; | ||
exports.name = 'removeUnusedNS'; | ||
exports.type = 'full'; | ||
@@ -8,0 +10,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'removeUselessDefs'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.name = 'removeUselessStrokeAndFill'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'removeViewBox'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.name = 'removeXMLNS'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
'use strict'; | ||
exports.type = 'perItem'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeXMLProcInst'; | ||
exports.type = 'visitor'; | ||
exports.active = true; | ||
exports.description = 'removes XML processing instructions'; | ||
@@ -15,12 +16,14 @@ | ||
* | ||
* @param {Object} item current iteration item | ||
* @return {Boolean} if false, item will be filtered out | ||
* | ||
* @author Kir Belevich | ||
*/ | ||
exports.fn = function (item) { | ||
if (item.type === 'instruction' && item.name === 'xml') { | ||
return false; | ||
} | ||
return true; | ||
exports.fn = () => { | ||
return { | ||
instruction: { | ||
enter: (node, parentNode) => { | ||
if (node.name === 'xml') { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |
@@ -6,2 +6,4 @@ 'use strict'; | ||
exports.name = 'reusePaths'; | ||
exports.type = 'full'; | ||
@@ -8,0 +10,0 @@ |
@@ -5,2 +5,4 @@ 'use strict'; | ||
exports.name = 'sortAttrs'; | ||
exports.type = 'perItem'; | ||
@@ -7,0 +9,0 @@ |
'use strict'; | ||
exports.name = 'sortDefsChildren'; | ||
exports.type = 'perItem'; | ||
@@ -4,0 +6,0 @@ |
299
README.md
@@ -5,3 +5,2 @@ <div align="center"> | ||
## SVGO [](https://npmjs.org/package/svgo) [](https://discord.gg/z8jX8NYxrE) | ||
@@ -57,4 +56,4 @@ | ||
pretty: true, // boolean, false by default | ||
} | ||
} | ||
}, | ||
}; | ||
``` | ||
@@ -72,3 +71,3 @@ | ||
{ | ||
name: 'builtinPluginName' | ||
name: 'builtinPluginName', | ||
}, | ||
@@ -79,87 +78,73 @@ // some plugins allow/require to pass options | ||
params: { | ||
optionName: 'optionValue' | ||
} | ||
} | ||
] | ||
} | ||
optionName: 'optionValue', | ||
}, | ||
}, | ||
], | ||
}; | ||
``` | ||
The default list is fully overridden if the `plugins` field is specified. To extend the default | ||
list use the `extendDefaultPlugins` utility: | ||
The default preset of plugins is fully overridden if the `plugins` field is specified. | ||
Use `preset-default` plugin to customize plugins options. | ||
```js | ||
const { extendDefaultPlugins } = require('svgo'); | ||
module.exports = { | ||
plugins: extendDefaultPlugins([ | ||
plugins: [ | ||
{ | ||
name: 'builtinPluginName', | ||
name: 'preset-default', | ||
params: { | ||
optionName: 'optionValue' | ||
} | ||
} | ||
]) | ||
} | ||
overrides: { | ||
// customize options | ||
builtinPluginName: { | ||
optionName: 'optionValue', | ||
}, | ||
// or disable plugins | ||
anotherBuiltinPlugin: false, | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
``` | ||
To disable one of the default plugins use the `active` field: | ||
Default preset includes the following list of plugins: | ||
```js | ||
const { extendDefaultPlugins } = require('svgo'); | ||
module.exports = { | ||
plugins: extendDefaultPlugins([ | ||
{ | ||
name: 'builtinPluginName', | ||
active: false | ||
} | ||
]) | ||
} | ||
``` | ||
- removeDoctype | ||
- removeXMLProcInst | ||
- removeComments | ||
- removeMetadata | ||
- removeEditorsNSData | ||
- cleanupAttrs | ||
- mergeStyles | ||
- inlineStyles | ||
- minifyStyles | ||
- cleanupIDs | ||
- removeUselessDefs | ||
- cleanupNumericValues | ||
- convertColors | ||
- removeUnknownsAndDefaults | ||
- removeNonInheritableGroupAttrs | ||
- removeUselessStrokeAndFill | ||
- removeViewBox | ||
- cleanupEnableBackground | ||
- removeHiddenElems | ||
- removeEmptyText | ||
- convertShapeToPath | ||
- convertEllipseToCircle | ||
- moveElemsAttrsToGroup | ||
- moveGroupAttrsToElems | ||
- collapseGroups | ||
- convertPathData | ||
- convertTransform | ||
- removeEmptyAttrs | ||
- removeEmptyContainers | ||
- mergePaths | ||
- removeUnusedNS | ||
- sortDefsChildren | ||
- removeTitle | ||
- removeDesc | ||
See the list of the default plugins: | ||
```js | ||
module.exports = { | ||
plugins: [ | ||
'removeDoctype', | ||
'removeXMLProcInst', | ||
'removeComments', | ||
'removeMetadata', | ||
'removeEditorsNSData', | ||
'cleanupAttrs', | ||
'mergeStyles', | ||
'inlineStyles', | ||
'minifyStyles', | ||
'cleanupIDs', | ||
'removeUselessDefs', | ||
'cleanupNumericValues', | ||
'convertColors', | ||
'removeUnknownsAndDefaults', | ||
'removeNonInheritableGroupAttrs', | ||
'removeUselessStrokeAndFill', | ||
'removeViewBox', | ||
'cleanupEnableBackground', | ||
'removeHiddenElems', | ||
'removeEmptyText', | ||
'convertShapeToPath', | ||
'convertEllipseToCircle', | ||
'moveElemsAttrsToGroup', | ||
'moveGroupAttrsToElems', | ||
'collapseGroups', | ||
'convertPathData', | ||
'convertTransform', | ||
'removeEmptyAttrs', | ||
'removeEmptyContainers', | ||
'mergePaths', | ||
'removeUnusedNS', | ||
'sortDefsChildren', | ||
'removeTitle', | ||
'removeDesc' | ||
] | ||
} | ||
``` | ||
It's also possible to specify a custom plugin: | ||
```js | ||
const anotherCustomPlugin = require('./another-custom-plugin.js') | ||
const anotherCustomPlugin = require('./another-custom-plugin.js'); | ||
module.exports = { | ||
@@ -173,7 +158,7 @@ plugins: [ | ||
}, | ||
fn: (ast, params, info) => {} | ||
fn: (ast, params, info) => {}, | ||
}, | ||
anotherCustomPlugin | ||
] | ||
} | ||
anotherCustomPlugin, | ||
], | ||
}; | ||
``` | ||
@@ -195,5 +180,5 @@ | ||
// all config fields are also available here | ||
multipass: true | ||
}) | ||
const optimizedSvgString = result.data | ||
multipass: true, | ||
}); | ||
const optimizedSvgString = result.data; | ||
``` | ||
@@ -207,6 +192,6 @@ | ||
const { loadConfig } = require('svgo'); | ||
const config = await loadConfig() | ||
const config = await loadConfig(); | ||
// you can also specify a relative or absolute path and customize the current working directory | ||
const config = await loadConfig(configFile, cwd) | ||
const config = await loadConfig(configFile, cwd); | ||
``` | ||
@@ -216,75 +201,77 @@ | ||
| Plugin | Description | Default | | ||
| ------ | ----------- | ------- | | ||
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | `enabled` | | ||
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js) | merge multiple style elements into one | `enabled` | | ||
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` | | ||
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` | | ||
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` | | ||
| [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` | | ||
| [removeMetadata](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) | remove `<metadata>` | `enabled` | | ||
| [removeTitle](https://github.com/svg/svgo/blob/master/plugins/removeTitle.js) | remove `<title>` | `enabled` | | ||
| [removeDesc](https://github.com/svg/svgo/blob/master/plugins/removeDesc.js) | remove `<desc>` | `enabled` | | ||
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` | | ||
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` | | ||
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` | | ||
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` | | ||
| [removeHiddenElems](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) | remove hidden elements | `enabled` | | ||
| [removeEmptyText](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) | remove empty Text elements | `enabled` | | ||
| [removeEmptyContainers](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) | remove empty Container elements | `enabled` | | ||
| [removeViewBox](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) | remove `viewBox` attribute when possible | `enabled` | | ||
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) | remove or cleanup `enable-background` attribute when possible | `enabled` | | ||
| [minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js) | minify `<style>` elements content with [CSSO](https://github.com/css/csso) | `enabled` | | ||
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) | convert styles into attributes | `disabled ` | | ||
| [convertColors](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`) | `enabled` | | ||
| [convertPathData](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) | convert Path data to relative or absolute (whichever is shorter), convert one segment to another, trim useless delimiters, smart rounding, and much more | `enabled` | | ||
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` | | ||
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` | | ||
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` | | ||
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` | | ||
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` | | ||
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` | | ||
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs | `enabled` | | ||
| [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | `enabled` | | ||
| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) | `disabled` | | ||
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group | `enabled` | | ||
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements | `enabled` | | ||
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | collapse useless groups | `enabled` | | ||
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | remove raster images | `disabled` | | ||
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js) | merge multiple Paths into one | `enabled` | | ||
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | `enabled` | | ||
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | `enabled` | | ||
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | sort element attributes for epic readability | `disabled` | | ||
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | `enabled` | | ||
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first) | `disabled` | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` | | ||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` | | ||
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` | | ||
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | remove `<style>` elements | `disabled` | | ||
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | remove `<script>` elements | `disabled` | | ||
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links | `disabled` | | ||
| Plugin | Description | Default | | ||
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | ||
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | `enabled` | | ||
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js) | merge multiple style elements into one | `enabled` | | ||
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` | | ||
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` | | ||
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` | | ||
| [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` | | ||
| [removeMetadata](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) | remove `<metadata>` | `enabled` | | ||
| [removeTitle](https://github.com/svg/svgo/blob/master/plugins/removeTitle.js) | remove `<title>` | `enabled` | | ||
| [removeDesc](https://github.com/svg/svgo/blob/master/plugins/removeDesc.js) | remove `<desc>` | `enabled` | | ||
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` | | ||
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` | | ||
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` | | ||
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` | | ||
| [removeHiddenElems](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) | remove hidden elements | `enabled` | | ||
| [removeEmptyText](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) | remove empty Text elements | `enabled` | | ||
| [removeEmptyContainers](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) | remove empty Container elements | `enabled` | | ||
| [removeViewBox](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) | remove `viewBox` attribute when possible | `enabled` | | ||
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) | remove or cleanup `enable-background` attribute when possible | `enabled` | | ||
| [minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js) | minify `<style>` elements content with [CSSO](https://github.com/css/csso) | `enabled` | | ||
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) | convert styles into attributes | `disabled` | | ||
| [convertColors](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`) | `enabled` | | ||
| [convertPathData](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) | convert Path data to relative or absolute (whichever is shorter), convert one segment to another, trim useless delimiters, smart rounding, and much more | `enabled` | | ||
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` | | ||
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` | | ||
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` | | ||
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` | | ||
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` | | ||
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` | | ||
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs | `enabled` | | ||
| [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | `enabled` | | ||
| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) | `disabled` | | ||
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group | `enabled` | | ||
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements | `enabled` | | ||
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | collapse useless groups | `enabled` | | ||
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | remove raster images | `disabled` | | ||
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js) | merge multiple Paths into one | `enabled` | | ||
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | `enabled` | | ||
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | `enabled` | | ||
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | sort element attributes for epic readability | `disabled` | | ||
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | `enabled` | | ||
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first) | `disabled` | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` | | ||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` | | ||
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` | | ||
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | remove `<style>` elements | `disabled` | | ||
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | remove `<script>` elements | `disabled` | | ||
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links | `disabled` | | ||
## Other Ways to Use SVGO | ||
* as a web app β [SVGOMG](https://jakearchibald.github.io/svgomg/) | ||
* as a GitHub Action β [SVGO Action](https://github.com/marketplace/actions/svgo-action) | ||
* as a Node.js module β [examples](https://github.com/svg/svgo/tree/master/examples) | ||
* as a Grunt task β [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin) | ||
* as a Gulp task β [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) | ||
* as a Mimosa module β [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg) | ||
* as an OSX Folder Action β [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action) | ||
* as a webpack loader β [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader) | ||
* as a Telegram Bot β [svgo_bot](https://github.com/maksugr/svgo_bot) | ||
* as a PostCSS plugin β [postcss-svgo](https://github.com/ben-eb/postcss-svgo) | ||
* as an Inkscape plugin β [inkscape-svgo](https://github.com/konsumer/inkscape-svgo) | ||
* as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor) | ||
* as macOS app - [Image Shrinker](https://image-shrinker.com) | ||
* as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo) | ||
* as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo) | ||
* as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo) | ||
* as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo) | ||
* as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export) | ||
- as a web app β [SVGOMG](https://jakearchibald.github.io/svgomg/) | ||
- as a GitHub Action β [SVGO Action](https://github.com/marketplace/actions/svgo-action) | ||
- as a Grunt task β [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin) | ||
- as a Gulp task β [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) | ||
- as a Mimosa module β [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg) | ||
- as an OSX Folder Action β [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action) | ||
- as a webpack loader β [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader) | ||
- as a Telegram Bot β [svgo_bot](https://github.com/maksugr/svgo_bot) | ||
- as a PostCSS plugin β [postcss-svgo](https://github.com/ben-eb/postcss-svgo) | ||
- as an Inkscape plugin β [inkscape-svgo](https://github.com/konsumer/inkscape-svgo) | ||
- as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor) | ||
- as a macOS app - [Image Shrinker](https://image-shrinker.com) | ||
- as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo) | ||
- as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo) | ||
- as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo) | ||
- as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo) | ||
- as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export) | ||
- as a Linux app - [Oh My SVG](https://github.com/sonnyp/OhMySVG) | ||
- as a Browser extension - [SVG Gobbler](https://github.com/rossmoody/svg-gobbler) | ||
- as an API - [Vector Express](https://github.com/smidyo/vectorexpress-api#convertor-svgo) | ||
@@ -294,4 +281,4 @@ ## Donators | ||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/master/fontello-image.svg" width="80">](https://fontello.com/) | | ||
|:-:|:-:| | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) | | ||
| :------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: | | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) | | ||
@@ -298,0 +285,0 @@ ## License and Copyright |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
921718
1.33%17
-10.53%77
-2.53%13780
-2.08%281
-4.42%+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed