Comparing version 2.2.0 to 2.2.1
@@ -11,3 +11,3 @@ 'use strict'; | ||
* | ||
* @param {Object} cssAst css-tree AST to flatten | ||
* @param {import('css-tree').CssNode} cssAst css-tree AST to flatten | ||
* @return {Array} selectors | ||
@@ -33,3 +33,3 @@ */ | ||
rule: rule, | ||
pseudos: [], | ||
pseudos: /** @type {{item: any; list: any[]}[]} */ ([]), | ||
}; | ||
@@ -113,3 +113,3 @@ | ||
* @param {Array} selectors to clean | ||
* @return {Array} Selectors without pseudo-elements and/or -classes | ||
* @return {void} | ||
*/ | ||
@@ -130,3 +130,3 @@ function cleanPseudos(selectors) { | ||
* @param {Array} bSpecificity Specificity of selector B | ||
* @return {Number} Score of selector specificity A compared to selector specificity B | ||
* @return {number} Score of selector specificity A compared to selector specificity B | ||
*/ | ||
@@ -150,3 +150,3 @@ function compareSpecificity(aSpecificity, bSpecificity) { | ||
* @param {Object} bSimpleSelectorNode Simple selector B | ||
* @return {Number} Score of selector A compared to selector B | ||
* @return {number} Score of selector A compared to selector B | ||
*/ | ||
@@ -176,3 +176,3 @@ function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) { | ||
* | ||
* @param {Object} declaration css-tree style declaration | ||
* @param {import('css-tree').CssNode} declaration css-tree style declaration | ||
* @return {Object} CSSStyleDeclaration property | ||
@@ -194,4 +194,4 @@ */ | ||
* | ||
* @param {Object} element style element | ||
* @return {String|Array} CSS string or empty array if no styles are set | ||
* @param {Object} elem style element | ||
* @return {string|Array} CSS string or empty array if no styles are set | ||
*/ | ||
@@ -205,4 +205,4 @@ function getCssStr(elem) { | ||
* | ||
* @param {Object} element style element | ||
* @param {String} CSS string to be set | ||
* @param {Object} elem style element | ||
* @param {string} css string to be set | ||
* @return {Object} reference to field with CSS | ||
@@ -209,0 +209,0 @@ */ |
108
lib/path.js
@@ -28,2 +28,5 @@ 'use strict'; | ||
/** | ||
* @param {string} c | ||
*/ | ||
const isCommand = (c) => { | ||
@@ -33,2 +36,5 @@ return c in argsCountPerCommand; | ||
/** | ||
* @param {string} c | ||
*/ | ||
const isWsp = (c) => { | ||
@@ -44,12 +50,26 @@ const codePoint = c.codePointAt(0); | ||
/** | ||
* @param {string} c | ||
*/ | ||
const isDigit = (c) => { | ||
const codePoint = c.codePointAt(0); | ||
if (codePoint == null) { | ||
return false; | ||
} | ||
return 48 <= codePoint && codePoint <= 57; | ||
}; | ||
/** | ||
* @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState | ||
*/ | ||
/** | ||
* @param {string} string | ||
* @param {number} cursor | ||
* @return {[number, number | null]} | ||
*/ | ||
const readNumber = (string, cursor) => { | ||
let i = cursor; | ||
let value = ''; | ||
// none | sign | whole | decimal_point | decimal | e | exponent_sign | exponent | ||
let state = 'none'; | ||
let state = /** @type {ReadNumberState} */ ('none'); | ||
for (; i < string.length; i += 1) { | ||
@@ -64,3 +84,3 @@ const c = string[i]; | ||
if (state === 'e') { | ||
state === 'exponent_sign'; | ||
state = 'exponent_sign'; | ||
value += c; | ||
@@ -116,11 +136,13 @@ continue; | ||
/** | ||
* @param {string} string | ||
*/ | ||
const parsePathData = (string) => { | ||
const pathData = []; | ||
let i = 0; | ||
let command = null; | ||
let args; | ||
let argsCount; | ||
let args = /** @type {number[]} */ ([]); | ||
let argsCount = 0; | ||
let canHaveComma = false; | ||
let hadComma = false; | ||
for (; i < string.length; i += 1) { | ||
for (let i = 0; i < string.length; i += 1) { | ||
const c = string.charAt(i); | ||
@@ -217,2 +239,11 @@ if (isWsp(c)) { | ||
/** | ||
* @typedef {{ | ||
* number: number; | ||
* precision?: number; | ||
* }} StringifyNumberOptions | ||
*/ | ||
/** | ||
* @param {StringifyNumberOptions} param | ||
*/ | ||
const stringifyNumber = ({ number, precision }) => { | ||
@@ -225,3 +256,3 @@ let result; | ||
if (result.includes('.')) { | ||
result = result.replace(/\.?0+$/, '') | ||
result = result.replace(/\.?0+$/, ''); | ||
} | ||
@@ -236,14 +267,40 @@ } | ||
// elliptical arc large-arc and sweep flags are rendered with spaces | ||
// because many non-browser environments are not able to parse such paths | ||
const stringifyArgs = ({ args, precision }) => { | ||
/** | ||
* @typedef {{ | ||
* command: string; | ||
* args: number[]; | ||
* precision?: number; | ||
* disableSpaceAfterFlags?: boolean; | ||
* }} StringifyArgsOptions | ||
*/ | ||
/** | ||
* | ||
* Elliptical arc large-arc and sweep flags are rendered with spaces | ||
* because many non-browser environments are not able to parse such paths | ||
* | ||
* @param {StringifyArgsOptions} param | ||
*/ | ||
const stringifyArgs = ({ | ||
command, | ||
args, | ||
precision, | ||
disableSpaceAfterFlags, | ||
}) => { | ||
let result = ''; | ||
let prev; | ||
let prev = ''; | ||
for (let i = 0; i < args.length; i += 1) { | ||
const number = args[i]; | ||
const numberString = stringifyNumber({ number, precision }); | ||
// avoid space before first and negative numbers | ||
if (i === 0 || numberString.startsWith('-')) { | ||
if ( | ||
disableSpaceAfterFlags && | ||
(command === 'A' || command === 'a') && | ||
(i === 4 || i === 5) | ||
) { | ||
result += numberString; | ||
} else if (i === 0 || numberString.startsWith('-')) { | ||
// avoid space before first and negative numbers | ||
result += numberString; | ||
} else if (prev.includes('.') && numberString.startsWith('.')) { | ||
// remove space before decimal with zero whole | ||
// only when previous number is also decimal | ||
result += numberString; | ||
@@ -258,3 +315,20 @@ } else { | ||
const stringifyPathData = ({ pathData, precision }) => { | ||
/** | ||
* | ||
* @typedef {{ | ||
* command: string; | ||
* args: number[]; | ||
* }} Command | ||
*/ | ||
/** | ||
* @typedef {{ | ||
* pathData: Command[]; | ||
* precision?: number; | ||
* disableSpaceAfterFlags?: boolean; | ||
* }} StringifyPathDataOptions | ||
*/ | ||
/** | ||
* @param {StringifyPathDataOptions} param | ||
*/ | ||
const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => { | ||
// combine sequence of the same commands | ||
@@ -293,3 +367,5 @@ let combined = []; | ||
for (const { command, args } of combined) { | ||
result += command + stringifyArgs({ args, precision }); | ||
result += | ||
command + | ||
stringifyArgs({ command, args, precision, disableSpaceAfterFlags }); | ||
} | ||
@@ -296,0 +372,0 @@ return result; |
@@ -50,2 +50,7 @@ 'use strict'; | ||
}); | ||
it('should stop on invalid scientific notation', () => { | ||
expect(parsePathData('M 0 5e++1 L 0 0')).to.deep.equal([ | ||
{ command: 'M', args: [0, 5] }, | ||
]); | ||
}); | ||
it('should stop on invalid numbers', () => { | ||
@@ -62,4 +67,4 @@ expect(parsePathData('M ...')).to.deep.equal([]); | ||
25,50 -30 0,1 50,-25 | ||
25,75 -30 0,1 50,-25 | ||
a25,100 -30 0,1 50,-25 | ||
25,75 -30 01.2,-25 | ||
a25,100 -30 0150,-25 | ||
l 50,-25 | ||
@@ -73,3 +78,3 @@ ` | ||
{ command: 'a', args: [25, 50, -30, 0, 1, 50, -25] }, | ||
{ command: 'a', args: [25, 75, -30, 0, 1, 50, -25] }, | ||
{ command: 'a', args: [25, 75, -30, 0, 1, 0.2, -25] }, | ||
{ command: 'a', args: [25, 100, -30, 0, 1, 50, -25] }, | ||
@@ -164,2 +169,21 @@ { command: 'l', args: [50, -25] }, | ||
}); | ||
it('allows to avoid spaces after arc flags', () => { | ||
const pathData = [ | ||
{ command: 'M', args: [0, 0] }, | ||
{ command: 'A', args: [50, 50, 10, 1, 0, 0.2, 20] }, | ||
{ command: 'a', args: [50, 50, 10, 1, 0, 0.2, 20] }, | ||
]; | ||
expect( | ||
stringifyPathData({ | ||
pathData, | ||
disableSpaceAfterFlags: false, | ||
}) | ||
).to.equal('M0 0A50 50 10 1 0 .2 20a50 50 10 1 0 .2 20'); | ||
expect( | ||
stringifyPathData({ | ||
pathData, | ||
disableSpaceAfterFlags: true, | ||
}) | ||
).to.equal('M0 0A50 50 10 10.2 20a50 50 10 10.2 20'); | ||
}); | ||
}); |
@@ -104,3 +104,3 @@ 'use strict'; | ||
...plugin, | ||
params: { configParams, ...plugin.params }, | ||
params: { ...configParams, ...plugin.params }, | ||
}; | ||
@@ -107,0 +107,0 @@ } else { |
'use strict'; | ||
var baseCssAdapter = require('css-select-base-adapter'); | ||
/** | ||
* DOMUtils API for SVGO AST (used by css-select) | ||
* @param {any} node | ||
* @return {node is any} | ||
*/ | ||
var svgoCssSelectAdapterMin = { | ||
// is the node a tag? | ||
// isTag: ( node:Node ) => isTag:Boolean | ||
isTag: function (node) { | ||
return node.isElem(); | ||
}, | ||
const isTag = (node) => { | ||
return node.isElem(); | ||
}; | ||
// get the parent of the node | ||
// getParent: ( node:Node ) => parentNode:Node | ||
// returns null when no parent exists | ||
getParent: function (node) { | ||
return node.parentNode || null; | ||
}, | ||
const existsOne = (test, elems) => { | ||
return elems.some((elem) => { | ||
if (isTag(elem)) { | ||
return test(elem) || existsOne(test, getChildren(elem)); | ||
} else { | ||
return false; | ||
} | ||
}); | ||
}; | ||
// get the node's children | ||
// getChildren: ( node:Node ) => children:[Node] | ||
getChildren: function (node) { | ||
return node.content || []; | ||
}, | ||
const getAttributeValue = (elem, name) => { | ||
return elem.hasAttr(name) ? elem.attr(name).value : undefined; | ||
}; | ||
// get the name of the tag | ||
// getName: ( elem:ElementNode ) => tagName:String | ||
getName: function (elemAst) { | ||
return elemAst.elem; | ||
}, | ||
const getChildren = (node) => { | ||
return node.content || []; | ||
}; | ||
// get the text content of the node, and its children if it has any | ||
// getText: ( node:Node ) => text:String | ||
// returns empty string when there is no text | ||
getText: function (node) { | ||
return node.content[0].text || node.content[0].cdata || ''; | ||
}, | ||
const getName = (elemAst) => { | ||
return elemAst.elem; | ||
}; | ||
// get the attribute value | ||
// getAttributeValue: ( elem:ElementNode, name:String ) => value:String | ||
// returns null when attribute doesn't exist | ||
getAttributeValue: function (elem, name) { | ||
return elem.hasAttr(name) ? elem.attr(name).value : null; | ||
}, | ||
const getParent = (node) => { | ||
return node.parentNode || null; | ||
}; | ||
// use base adapter for default implementation | ||
var svgoCssSelectAdapter = baseCssAdapter(svgoCssSelectAdapterMin); | ||
const getSiblings = (elem) => { | ||
var parent = getParent(elem); | ||
return parent ? getChildren(parent) : []; | ||
}; | ||
const getText = (node) => { | ||
return node.content[0].text || node.content[0].cdata || ''; | ||
}; | ||
const hasAttrib = (elem, name) => { | ||
return getAttributeValue(elem, name) !== undefined; | ||
}; | ||
const removeSubsets = (nodes) => { | ||
let idx = nodes.length; | ||
let node; | ||
let ancestor; | ||
let replace; | ||
// Check if each node (or one of its ancestors) is already contained in the | ||
// array. | ||
while (--idx > -1) { | ||
node = ancestor = nodes[idx]; | ||
// Temporarily remove the node under consideration | ||
nodes[idx] = null; | ||
replace = true; | ||
while (ancestor) { | ||
if (nodes.includes(ancestor)) { | ||
replace = false; | ||
nodes.splice(idx, 1); | ||
break; | ||
} | ||
ancestor = getParent(ancestor); | ||
} | ||
// If the node has been found to be unique, re-insert it. | ||
if (replace) { | ||
nodes[idx] = node; | ||
} | ||
} | ||
return nodes; | ||
}; | ||
const findAll = (test, elems) => { | ||
const result = []; | ||
for (const elem of elems) { | ||
if (isTag(elem)) { | ||
if (test(elem)) { | ||
result.push(elem); | ||
} | ||
result.push(...findAll(test, getChildren(elem))); | ||
} | ||
} | ||
return result; | ||
}; | ||
const findOne = (test, elems) => { | ||
for (const elem of elems) { | ||
if (isTag(elem)) { | ||
if (test(elem)) { | ||
return elem; | ||
} | ||
const result = findOne(test, getChildren(elem)); | ||
if (result) { | ||
return result; | ||
} | ||
} | ||
} | ||
return null; | ||
}; | ||
const svgoCssSelectAdapter = { | ||
isTag, | ||
existsOne, | ||
getAttributeValue, | ||
getChildren, | ||
getName, | ||
getParent, | ||
getSiblings, | ||
getText, | ||
hasAttrib, | ||
removeSubsets, | ||
findAll, | ||
findOne, | ||
}; | ||
module.exports = svgoCssSelectAdapter; |
@@ -142,3 +142,3 @@ 'use strict'; | ||
* | ||
* @return {String} Textual representation of the declaration block (empty string for no properties) | ||
* @return {string} Textual representation of the declaration block (empty string for no properties) | ||
*/ | ||
@@ -187,4 +187,4 @@ CSSStyleDeclaration.prototype.getCssText = function () { | ||
* | ||
* @param {String} propertyName representing the property name to be checked. | ||
* @return {String} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string. | ||
* @param {string} propertyName representing the property name to be checked. | ||
* @return {string} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string. | ||
*/ | ||
@@ -199,4 +199,4 @@ CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) { | ||
* | ||
* @param {String} propertyName representing the property name to be checked. | ||
* @return {String} value containing the value of the property. If not set, returns the empty string. | ||
* @param {string} propertyName representing the property name to be checked. | ||
* @return {string} value containing the value of the property. If not set, returns the empty string. | ||
*/ | ||
@@ -211,4 +211,4 @@ CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) { | ||
* | ||
* @param {Number} index of the node to be fetched. The index is zero-based. | ||
* @return {String} propertyName that is the name of the CSS property at the specified index. | ||
* @param {number} index of the node to be fetched. The index is zero-based. | ||
* @return {string} propertyName that is the name of the CSS property at the specified index. | ||
*/ | ||
@@ -241,4 +241,4 @@ CSSStyleDeclaration.prototype.item = function (index) { | ||
* | ||
* @param {String} propertyName representing the property name to be removed. | ||
* @return {String} oldValue equal to the value of the CSS property before it was removed. | ||
* @param {string} propertyName representing the property name to be removed. | ||
* @return {string} oldValue equal to the value of the CSS property before it was removed. | ||
*/ | ||
@@ -263,6 +263,6 @@ CSSStyleDeclaration.prototype.removeProperty = function (propertyName) { | ||
* | ||
* @param {String} propertyName representing the CSS property name to be modified. | ||
* @param {String} [value] containing the new property value. If not specified, treated as the empty string. value must not contain "!important" -- that should be set using the priority parameter. | ||
* @param {String} [priority] allowing the "important" CSS priority to be set. If not specified, treated as the empty string. | ||
* @return {undefined} | ||
* @param {string} propertyName representing the CSS property name to be modified. | ||
* @param {string} value containing the new property value. If not specified, treated as the empty string. value must not contain "!important" -- that should be set using the priority parameter. | ||
* @param {string} priority allowing the "important" CSS priority to be set. If not specified, treated as the empty string. | ||
* @return {{value: string, priority: string}} | ||
*/ | ||
@@ -269,0 +269,0 @@ CSSStyleDeclaration.prototype.setProperty = function ( |
@@ -10,3 +10,3 @@ 'use strict'; | ||
* @param {Object} info extra information | ||
* @param {Object} plugins plugins object from config | ||
* @param {Array} plugins plugins object from config | ||
* @return {Object} output data | ||
@@ -49,3 +49,3 @@ */ | ||
* @param {Array} plugins plugins list to process | ||
* @param {Boolean} [reverse] reverse pass? | ||
* @param {boolean} [reverse] reverse pass? | ||
* @return {Object} output data | ||
@@ -52,0 +52,0 @@ */ |
@@ -6,5 +6,5 @@ 'use strict'; | ||
* | ||
* @param {String} str input string | ||
* @param {String} type Data URI type | ||
* @return {String} output string | ||
* @param {string} str input string | ||
* @param {string} type Data URI type | ||
* @return {string} output string | ||
*/ | ||
@@ -31,3 +31,3 @@ exports.encodeSVGDatauri = function (str, type) { | ||
* @param {string} str input string | ||
* @return {String} output string | ||
* @return {string} output string | ||
*/ | ||
@@ -56,2 +56,6 @@ exports.decodeSVGDatauri = function (str) { | ||
/** | ||
* @param {any[]} a | ||
* @param {any[]} b | ||
*/ | ||
exports.intersectArrays = function (a, b) { | ||
@@ -71,3 +75,3 @@ return a.filter(function (n) { | ||
* @param {Object} params | ||
* @param {string?} command path data instruction | ||
* @param {string} [command] path data instruction | ||
* @return {string} | ||
@@ -97,5 +101,5 @@ */ | ||
// -0.5 → -.5 | ||
if (params.leadingZero) { | ||
item = removeLeadingZero(item); | ||
} | ||
const itemStr = params.leadingZero | ||
? removeLeadingZero(item) | ||
: item.toString(); | ||
@@ -107,3 +111,3 @@ // no extra space in front of negative number or | ||
delimiter != '' && | ||
(item < 0 || (String(item).charCodeAt(0) == 46 && prev % 1 !== 0)) | ||
(item < 0 || (itemStr.charCodeAt(0) == 46 && prev % 1 !== 0)) | ||
) { | ||
@@ -114,3 +118,3 @@ delimiter = ''; | ||
prev = item; | ||
str += delimiter + item; | ||
str += delimiter + itemStr; | ||
}); | ||
@@ -129,5 +133,5 @@ return str; | ||
* | ||
* @param {Float} num input number | ||
* @param {number} num input number | ||
* | ||
* @return {String} output number as string | ||
* @return {string} output number as string | ||
*/ | ||
@@ -134,0 +138,0 @@ var removeLeadingZero = function (num) { |
{ | ||
"name": "svgo", | ||
"version": "2.2.0", | ||
"version": "2.2.1", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -54,3 +54,5 @@ "keywords": [ | ||
"lint": "eslint .", | ||
"typecheck": "tsc", | ||
"test-browser": "rollup -c && node ./test/browser.js", | ||
"test-regression": "NO_DIFF=1 node ./test/regression.js", | ||
"prepublishOnly": "rm -rf dist && rollup -c" | ||
@@ -101,3 +103,2 @@ }, | ||
"css-select": "^3.1.2", | ||
"css-select-base-adapter": "^0.1.1", | ||
"css-tree": "^1.1.2", | ||
@@ -111,2 +112,3 @@ "csso": "^4.2.0", | ||
"@rollup/plugin-node-resolve": "^11.2.0", | ||
"@types/mocha": "^8.2.1", | ||
"c8": "^7.6.0", | ||
@@ -116,7 +118,13 @@ "chai": "^4.3.0", | ||
"eslint": "^7.20.0", | ||
"get-stream": "^6.0.0", | ||
"mocha": "^8.3.0", | ||
"mock-stdin": "^1.0.0", | ||
"node-fetch": "^2.6.1", | ||
"pixelmatch": "^5.2.1", | ||
"playwright": "^1.8.1", | ||
"pngjs": "^6.0.0", | ||
"prettier": "^2.2.1", | ||
"rollup": "^2.39.0" | ||
"rollup": "^2.39.0", | ||
"tar-stream": "^2.2.0", | ||
"typescript": "^4.2.2" | ||
}, | ||
@@ -123,0 +131,0 @@ "engines": { |
@@ -5,13 +5,81 @@ 'use strict'; | ||
exports.elemsGroups = { | ||
animation: ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'], | ||
descriptive: ['desc', 'metadata', 'title'], | ||
shape: ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'], | ||
structural: ['defs', 'g', 'svg', 'symbol', 'use'], | ||
paintServer: ['solidColor', 'linearGradient', 'radialGradient', 'meshGradient', 'pattern', 'hatch'], | ||
nonRendering: ['linearGradient', 'radialGradient', 'pattern', 'clipPath', 'mask', 'marker', 'symbol', 'filter', 'solidColor'], | ||
container: ['a', 'defs', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol', 'foreignObject'], | ||
textContent: ['altGlyph', 'altGlyphDef', 'altGlyphItem', 'glyph', 'glyphRef', 'textPath', 'text', 'tref', 'tspan'], | ||
textContentChild: ['altGlyph', 'textPath', 'tref', 'tspan'], | ||
lightSource: ['feDiffuseLighting', 'feSpecularLighting', 'feDistantLight', 'fePointLight', 'feSpotLight'], | ||
filterPrimitive: ['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feFlood', 'feGaussianBlur', 'feImage', 'feMerge', 'feMorphology', 'feOffset', 'feSpecularLighting', 'feTile', 'feTurbulence'] | ||
animation: [ | ||
'animate', | ||
'animateColor', | ||
'animateMotion', | ||
'animateTransform', | ||
'set', | ||
], | ||
descriptive: ['desc', 'metadata', 'title'], | ||
shape: ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'], | ||
structural: ['defs', 'g', 'svg', 'symbol', 'use'], | ||
paintServer: [ | ||
'solidColor', | ||
'linearGradient', | ||
'radialGradient', | ||
'meshGradient', | ||
'pattern', | ||
'hatch', | ||
], | ||
nonRendering: [ | ||
'linearGradient', | ||
'radialGradient', | ||
'pattern', | ||
'clipPath', | ||
'mask', | ||
'marker', | ||
'symbol', | ||
'filter', | ||
'solidColor', | ||
], | ||
container: [ | ||
'a', | ||
'defs', | ||
'g', | ||
'marker', | ||
'mask', | ||
'missing-glyph', | ||
'pattern', | ||
'svg', | ||
'switch', | ||
'symbol', | ||
'foreignObject', | ||
], | ||
textContent: [ | ||
'altGlyph', | ||
'altGlyphDef', | ||
'altGlyphItem', | ||
'glyph', | ||
'glyphRef', | ||
'textPath', | ||
'text', | ||
'tref', | ||
'tspan', | ||
], | ||
textContentChild: ['altGlyph', 'textPath', 'tref', 'tspan'], | ||
lightSource: [ | ||
'feDiffuseLighting', | ||
'feSpecularLighting', | ||
'feDistantLight', | ||
'fePointLight', | ||
'feSpotLight', | ||
], | ||
filterPrimitive: [ | ||
'feBlend', | ||
'feColorMatrix', | ||
'feComponentTransfer', | ||
'feComposite', | ||
'feConvolveMatrix', | ||
'feDiffuseLighting', | ||
'feDisplacementMap', | ||
'feFlood', | ||
'feGaussianBlur', | ||
'feImage', | ||
'feMerge', | ||
'feMorphology', | ||
'feOffset', | ||
'feSpecularLighting', | ||
'feTile', | ||
'feTurbulence', | ||
], | ||
}; | ||
@@ -25,137 +93,198 @@ | ||
exports.attrsGroups = { | ||
animationAddition: ['additive', 'accumulate'], | ||
animationAttributeTarget: ['attributeType', 'attributeName'], | ||
animationEvent: ['onbegin', 'onend', 'onrepeat', 'onload'], | ||
animationTiming: ['begin', 'dur', 'end', 'min', 'max', 'restart', 'repeatCount', 'repeatDur', 'fill'], | ||
animationValue: ['calcMode', 'values', 'keyTimes', 'keySplines', 'from', 'to', 'by'], | ||
conditionalProcessing: ['requiredFeatures', 'requiredExtensions', 'systemLanguage'], | ||
core: ['id', 'tabindex', 'xml:base', 'xml:lang', 'xml:space'], | ||
graphicalEvent: ['onfocusin', 'onfocusout', 'onactivate', 'onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onload'], | ||
presentation: [ | ||
'alignment-baseline', | ||
'baseline-shift', | ||
'clip', | ||
'clip-path', | ||
'clip-rule', | ||
'color', | ||
'color-interpolation', | ||
'color-interpolation-filters', | ||
'color-profile', | ||
'color-rendering', | ||
'cursor', | ||
'direction', | ||
'display', | ||
'dominant-baseline', | ||
'enable-background', | ||
'fill', | ||
'fill-opacity', | ||
'fill-rule', | ||
'filter', | ||
'flood-color', | ||
'flood-opacity', | ||
'font-family', | ||
'font-size', | ||
'font-size-adjust', | ||
'font-stretch', | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'glyph-orientation-horizontal', | ||
'glyph-orientation-vertical', | ||
'image-rendering', | ||
'letter-spacing', | ||
'lighting-color', | ||
'marker-end', | ||
'marker-mid', | ||
'marker-start', | ||
'mask', | ||
'opacity', | ||
'overflow', | ||
'paint-order', | ||
'pointer-events', | ||
'shape-rendering', | ||
'stop-color', | ||
'stop-opacity', | ||
'stroke', | ||
'stroke-dasharray', | ||
'stroke-dashoffset', | ||
'stroke-linecap', | ||
'stroke-linejoin', | ||
'stroke-miterlimit', | ||
'stroke-opacity', | ||
'stroke-width', | ||
'text-anchor', | ||
'text-decoration', | ||
'text-overflow', | ||
'text-rendering', | ||
'transform', | ||
'unicode-bidi', | ||
'vector-effect', | ||
'visibility', | ||
'word-spacing', | ||
'writing-mode' | ||
], | ||
xlink: ['xlink:href', 'xlink:show', 'xlink:actuate', 'xlink:type', 'xlink:role', 'xlink:arcrole', 'xlink:title'], | ||
documentEvent: ['onunload', 'onabort', 'onerror', 'onresize', 'onscroll', 'onzoom'], | ||
filterPrimitive: ['x', 'y', 'width', 'height', 'result'], | ||
transferFunction: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset'] | ||
animationAddition: ['additive', 'accumulate'], | ||
animationAttributeTarget: ['attributeType', 'attributeName'], | ||
animationEvent: ['onbegin', 'onend', 'onrepeat', 'onload'], | ||
animationTiming: [ | ||
'begin', | ||
'dur', | ||
'end', | ||
'min', | ||
'max', | ||
'restart', | ||
'repeatCount', | ||
'repeatDur', | ||
'fill', | ||
], | ||
animationValue: [ | ||
'calcMode', | ||
'values', | ||
'keyTimes', | ||
'keySplines', | ||
'from', | ||
'to', | ||
'by', | ||
], | ||
conditionalProcessing: [ | ||
'requiredFeatures', | ||
'requiredExtensions', | ||
'systemLanguage', | ||
], | ||
core: ['id', 'tabindex', 'xml:base', 'xml:lang', 'xml:space'], | ||
graphicalEvent: [ | ||
'onfocusin', | ||
'onfocusout', | ||
'onactivate', | ||
'onclick', | ||
'onmousedown', | ||
'onmouseup', | ||
'onmouseover', | ||
'onmousemove', | ||
'onmouseout', | ||
'onload', | ||
], | ||
presentation: [ | ||
'alignment-baseline', | ||
'baseline-shift', | ||
'clip', | ||
'clip-path', | ||
'clip-rule', | ||
'color', | ||
'color-interpolation', | ||
'color-interpolation-filters', | ||
'color-profile', | ||
'color-rendering', | ||
'cursor', | ||
'direction', | ||
'display', | ||
'dominant-baseline', | ||
'enable-background', | ||
'fill', | ||
'fill-opacity', | ||
'fill-rule', | ||
'filter', | ||
'flood-color', | ||
'flood-opacity', | ||
'font-family', | ||
'font-size', | ||
'font-size-adjust', | ||
'font-stretch', | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'glyph-orientation-horizontal', | ||
'glyph-orientation-vertical', | ||
'image-rendering', | ||
'letter-spacing', | ||
'lighting-color', | ||
'marker-end', | ||
'marker-mid', | ||
'marker-start', | ||
'mask', | ||
'opacity', | ||
'overflow', | ||
'paint-order', | ||
'pointer-events', | ||
'shape-rendering', | ||
'stop-color', | ||
'stop-opacity', | ||
'stroke', | ||
'stroke-dasharray', | ||
'stroke-dashoffset', | ||
'stroke-linecap', | ||
'stroke-linejoin', | ||
'stroke-miterlimit', | ||
'stroke-opacity', | ||
'stroke-width', | ||
'text-anchor', | ||
'text-decoration', | ||
'text-overflow', | ||
'text-rendering', | ||
'transform', | ||
'unicode-bidi', | ||
'vector-effect', | ||
'visibility', | ||
'word-spacing', | ||
'writing-mode', | ||
], | ||
xlink: [ | ||
'xlink:href', | ||
'xlink:show', | ||
'xlink:actuate', | ||
'xlink:type', | ||
'xlink:role', | ||
'xlink:arcrole', | ||
'xlink:title', | ||
], | ||
documentEvent: [ | ||
'onunload', | ||
'onabort', | ||
'onerror', | ||
'onresize', | ||
'onscroll', | ||
'onzoom', | ||
], | ||
filterPrimitive: ['x', 'y', 'width', 'height', 'result'], | ||
transferFunction: [ | ||
'type', | ||
'tableValues', | ||
'slope', | ||
'intercept', | ||
'amplitude', | ||
'exponent', | ||
'offset', | ||
], | ||
}; | ||
exports.attrsGroupsDefaults = { | ||
core: {'xml:space': 'default'}, | ||
filterPrimitive: {x: '0', y: '0', width: '100%', height: '100%'}, | ||
presentation: { | ||
clip: 'auto', | ||
'clip-path': 'none', | ||
'clip-rule': 'nonzero', | ||
mask: 'none', | ||
opacity: '1', | ||
'stop-color': '#000', | ||
'stop-opacity': '1', | ||
'fill-opacity': '1', | ||
'fill-rule': 'nonzero', | ||
fill: '#000', | ||
stroke: 'none', | ||
'stroke-width': '1', | ||
'stroke-linecap': 'butt', | ||
'stroke-linejoin': 'miter', | ||
'stroke-miterlimit': '4', | ||
'stroke-dasharray': 'none', | ||
'stroke-dashoffset': '0', | ||
'stroke-opacity': '1', | ||
'paint-order': 'normal', | ||
'vector-effect': 'none', | ||
display: 'inline', | ||
visibility: 'visible', | ||
'marker-start': 'none', | ||
'marker-mid': 'none', | ||
'marker-end': 'none', | ||
'color-interpolation': 'sRGB', | ||
'color-interpolation-filters': 'linearRGB', | ||
'color-rendering': 'auto', | ||
'shape-rendering': 'auto', | ||
'text-rendering': 'auto', | ||
'image-rendering': 'auto', | ||
'font-style': 'normal', | ||
'font-variant': 'normal', | ||
'font-weight': 'normal', | ||
'font-stretch': 'normal', | ||
'font-size': 'medium', | ||
'font-size-adjust': 'none', | ||
kerning: 'auto', | ||
'letter-spacing': 'normal', | ||
'word-spacing': 'normal', | ||
'text-decoration': 'none', | ||
'text-anchor': 'start', | ||
'text-overflow': 'clip', | ||
'writing-mode': 'lr-tb', | ||
'glyph-orientation-vertical': 'auto', | ||
'glyph-orientation-horizontal': '0deg', | ||
direction: 'ltr', | ||
'unicode-bidi': 'normal', | ||
'dominant-baseline': 'auto', | ||
'alignment-baseline': 'baseline', | ||
'baseline-shift': 'baseline' | ||
}, | ||
transferFunction: {slope: '1', intercept: '0', amplitude: '1', exponent: '1', offset: '0'} | ||
core: { 'xml:space': 'default' }, | ||
presentation: { | ||
clip: 'auto', | ||
'clip-path': 'none', | ||
'clip-rule': 'nonzero', | ||
mask: 'none', | ||
opacity: '1', | ||
'stop-color': '#000', | ||
'stop-opacity': '1', | ||
'fill-opacity': '1', | ||
'fill-rule': 'nonzero', | ||
fill: '#000', | ||
stroke: 'none', | ||
'stroke-width': '1', | ||
'stroke-linecap': 'butt', | ||
'stroke-linejoin': 'miter', | ||
'stroke-miterlimit': '4', | ||
'stroke-dasharray': 'none', | ||
'stroke-dashoffset': '0', | ||
'stroke-opacity': '1', | ||
'paint-order': 'normal', | ||
'vector-effect': 'none', | ||
display: 'inline', | ||
visibility: 'visible', | ||
'marker-start': 'none', | ||
'marker-mid': 'none', | ||
'marker-end': 'none', | ||
'color-interpolation': 'sRGB', | ||
'color-interpolation-filters': 'linearRGB', | ||
'color-rendering': 'auto', | ||
'shape-rendering': 'auto', | ||
'text-rendering': 'auto', | ||
'image-rendering': 'auto', | ||
'font-style': 'normal', | ||
'font-variant': 'normal', | ||
'font-weight': 'normal', | ||
'font-stretch': 'normal', | ||
'font-size': 'medium', | ||
'font-size-adjust': 'none', | ||
kerning: 'auto', | ||
'letter-spacing': 'normal', | ||
'word-spacing': 'normal', | ||
'text-decoration': 'none', | ||
'text-anchor': 'start', | ||
'text-overflow': 'clip', | ||
'writing-mode': 'lr-tb', | ||
'glyph-orientation-vertical': 'auto', | ||
'glyph-orientation-horizontal': '0deg', | ||
direction: 'ltr', | ||
'unicode-bidi': 'normal', | ||
'dominant-baseline': 'auto', | ||
'alignment-baseline': 'baseline', | ||
'baseline-shift': 'baseline', | ||
}, | ||
transferFunction: { | ||
slope: '1', | ||
intercept: '0', | ||
amplitude: '1', | ||
exponent: '1', | ||
offset: '0', | ||
}, | ||
}; | ||
@@ -165,2109 +294,1548 @@ | ||
exports.elems = { | ||
a: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'target' | ||
], | ||
defaults: { | ||
target: '_self' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
// not spec compliant | ||
'tspan', | ||
] | ||
a: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'target', | ||
], | ||
defaults: { | ||
target: '_self', | ||
}, | ||
altGlyph: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'glyphRef', | ||
'format', | ||
'rotate' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
// not spec compliant | ||
'tspan', | ||
], | ||
}, | ||
altGlyph: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'glyphRef', | ||
'format', | ||
'rotate', | ||
], | ||
}, | ||
altGlyphDef: { | ||
attrsGroups: ['core'], | ||
content: ['glyphRef'], | ||
}, | ||
altGlyphItem: { | ||
attrsGroups: ['core'], | ||
content: ['glyphRef', 'altGlyphItem'], | ||
}, | ||
animate: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationAddition', | ||
'animationAttributeTarget', | ||
'animationEvent', | ||
'animationTiming', | ||
'animationValue', | ||
'presentation', | ||
'xlink', | ||
], | ||
attrs: ['externalResourcesRequired'], | ||
contentGroups: ['descriptive'], | ||
}, | ||
animateColor: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationEvent', | ||
'xlink', | ||
'animationAttributeTarget', | ||
'animationTiming', | ||
'animationValue', | ||
'animationAddition', | ||
'presentation', | ||
], | ||
attrs: ['externalResourcesRequired'], | ||
contentGroups: ['descriptive'], | ||
}, | ||
animateMotion: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationEvent', | ||
'xlink', | ||
'animationTiming', | ||
'animationValue', | ||
'animationAddition', | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'path', | ||
'keyPoints', | ||
'rotate', | ||
'origin', | ||
], | ||
defaults: { | ||
rotate: '0', | ||
}, | ||
altGlyphDef: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
content: [ | ||
'glyphRef' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: ['mpath'], | ||
}, | ||
animateTransform: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationEvent', | ||
'xlink', | ||
'animationAttributeTarget', | ||
'animationTiming', | ||
'animationValue', | ||
'animationAddition', | ||
], | ||
attrs: ['externalResourcesRequired', 'type'], | ||
contentGroups: ['descriptive'], | ||
}, | ||
circle: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'cx', | ||
'cy', | ||
'r', | ||
], | ||
defaults: { | ||
cx: '0', | ||
cy: '0', | ||
}, | ||
altGlyphItem: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
content: [ | ||
'glyphRef', | ||
'altGlyphItem' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
clipPath: { | ||
attrsGroups: ['conditionalProcessing', 'core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'clipPathUnits', | ||
], | ||
defaults: { | ||
clipPathUnits: 'userSpaceOnUse', | ||
}, | ||
animate: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationAddition', | ||
'animationAttributeTarget', | ||
'animationEvent', | ||
'animationTiming', | ||
'animationValue', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
contentGroups: ['animation', 'descriptive', 'shape'], | ||
content: ['text', 'use'], | ||
}, | ||
'color-profile': { | ||
attrsGroups: ['core', 'xlink'], | ||
attrs: ['local', 'name', 'rendering-intent'], | ||
defaults: { | ||
name: 'sRGB', | ||
'rendering-intent': 'auto', | ||
}, | ||
animateColor: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationEvent', | ||
'xlink', | ||
'animationAttributeTarget', | ||
'animationTiming', | ||
'animationValue', | ||
'animationAddition', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
contentGroups: ['descriptive'], | ||
}, | ||
cursor: { | ||
attrsGroups: ['core', 'conditionalProcessing', 'xlink'], | ||
attrs: ['externalResourcesRequired', 'x', 'y'], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
}, | ||
animateMotion: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationEvent', | ||
'xlink', | ||
'animationTiming', | ||
'animationValue', | ||
'animationAddition' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'path', | ||
'keyPoints', | ||
'rotate', | ||
'origin' | ||
], | ||
defaults: { | ||
'rotate': '0' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'mpath' | ||
] | ||
contentGroups: ['descriptive'], | ||
}, | ||
defs: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: ['class', 'style', 'externalResourcesRequired', 'transform'], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
desc: { | ||
attrsGroups: ['core'], | ||
attrs: ['class', 'style'], | ||
}, | ||
ellipse: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'cx', | ||
'cy', | ||
'rx', | ||
'ry', | ||
], | ||
defaults: { | ||
cx: '0', | ||
cy: '0', | ||
}, | ||
animateTransform: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animationEvent', | ||
'xlink', | ||
'animationAttributeTarget', | ||
'animationTiming', | ||
'animationValue', | ||
'animationAddition' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'type' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
feBlend: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
// TODO: in - 'If no value is provided and this is the first filter primitive, | ||
// then this filter primitive will use SourceGraphic as its input' | ||
'in', | ||
'in2', | ||
'mode', | ||
], | ||
defaults: { | ||
mode: 'normal', | ||
}, | ||
circle: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'cx', | ||
'cy', | ||
'r' | ||
], | ||
defaults: { | ||
cx: '0', | ||
cy: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feColorMatrix: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in', 'type', 'values'], | ||
defaults: { | ||
type: 'matrix', | ||
}, | ||
clipPath: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'clipPathUnits' | ||
], | ||
defaults: { | ||
clipPathUnits: 'userSpaceOnUse' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape' | ||
], | ||
content: [ | ||
'text', | ||
'use' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feComponentTransfer: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in'], | ||
content: ['feFuncA', 'feFuncB', 'feFuncG', 'feFuncR'], | ||
}, | ||
feComposite: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in', 'in2', 'operator', 'k1', 'k2', 'k3', 'k4'], | ||
defaults: { | ||
operator: 'over', | ||
k1: '0', | ||
k2: '0', | ||
k3: '0', | ||
k4: '0', | ||
}, | ||
'color-profile': { | ||
attrsGroups: [ | ||
'core', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'local', | ||
'name', | ||
'rendering-intent' | ||
], | ||
defaults: { | ||
name: 'sRGB', | ||
'rendering-intent': 'auto' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feConvolveMatrix: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'order', | ||
'kernelMatrix', | ||
// TODO: divisor - 'The default value is the sum of all values in kernelMatrix, | ||
// with the exception that if the sum is zero, then the divisor is set to 1' | ||
'divisor', | ||
'bias', | ||
// TODO: targetX - 'By default, the convolution matrix is centered in X over each | ||
// pixel of the input image (i.e., targetX = floor ( orderX / 2 ))' | ||
'targetX', | ||
'targetY', | ||
'edgeMode', | ||
// TODO: kernelUnitLength - 'The first number is the <dx> value. The second number | ||
// is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>' | ||
'kernelUnitLength', | ||
'preserveAlpha', | ||
], | ||
defaults: { | ||
order: '3', | ||
bias: '0', | ||
edgeMode: 'duplicate', | ||
preserveAlpha: 'false', | ||
}, | ||
cursor: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'x', | ||
'y' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feDiffuseLighting: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'surfaceScale', | ||
'diffuseConstant', | ||
'kernelUnitLength', | ||
], | ||
defaults: { | ||
surfaceScale: '1', | ||
diffuseConstant: '1', | ||
}, | ||
defs: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: [ | ||
// TODO: 'exactly one light source element, in any order' | ||
'feDistantLight', | ||
'fePointLight', | ||
'feSpotLight', | ||
], | ||
}, | ||
feDisplacementMap: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'in2', | ||
'scale', | ||
'xChannelSelector', | ||
'yChannelSelector', | ||
], | ||
defaults: { | ||
scale: '0', | ||
xChannelSelector: 'A', | ||
yChannelSelector: 'A', | ||
}, | ||
desc: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feDistantLight: { | ||
attrsGroups: ['core'], | ||
attrs: ['azimuth', 'elevation'], | ||
defaults: { | ||
azimuth: '0', | ||
elevation: '0', | ||
}, | ||
ellipse: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'cx', | ||
'cy', | ||
'rx', | ||
'ry' | ||
], | ||
defaults: { | ||
cx: '0', | ||
cy: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feFlood: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style'], | ||
content: ['animate', 'animateColor', 'set'], | ||
}, | ||
feFuncA: { | ||
attrsGroups: ['core', 'transferFunction'], | ||
content: ['set', 'animate'], | ||
}, | ||
feFuncB: { | ||
attrsGroups: ['core', 'transferFunction'], | ||
content: ['set', 'animate'], | ||
}, | ||
feFuncG: { | ||
attrsGroups: ['core', 'transferFunction'], | ||
content: ['set', 'animate'], | ||
}, | ||
feFuncR: { | ||
attrsGroups: ['core', 'transferFunction'], | ||
content: ['set', 'animate'], | ||
}, | ||
feGaussianBlur: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in', 'stdDeviation'], | ||
defaults: { | ||
stdDeviation: '0', | ||
}, | ||
feBlend: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
// TODO: in - 'If no value is provided and this is the first filter primitive, | ||
// then this filter primitive will use SourceGraphic as its input' | ||
'in', | ||
'in2', | ||
'mode' | ||
], | ||
defaults: { | ||
mode: 'normal' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
content: ['set', 'animate'], | ||
}, | ||
feImage: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive', 'xlink'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
preserveAspectRatio: 'xMidYMid meet', | ||
}, | ||
feColorMatrix: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'type', | ||
'values' | ||
], | ||
defaults: { | ||
type: 'matrix' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
content: ['animate', 'animateTransform', 'set'], | ||
}, | ||
feMerge: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style'], | ||
content: ['feMergeNode'], | ||
}, | ||
feMergeNode: { | ||
attrsGroups: ['core'], | ||
attrs: ['in'], | ||
content: ['animate', 'set'], | ||
}, | ||
feMorphology: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in', 'operator', 'radius'], | ||
defaults: { | ||
operator: 'erode', | ||
radius: '0', | ||
}, | ||
feComponentTransfer: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in' | ||
], | ||
content: [ | ||
'feFuncA', | ||
'feFuncB', | ||
'feFuncG', | ||
'feFuncR' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feOffset: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in', 'dx', 'dy'], | ||
defaults: { | ||
dx: '0', | ||
dy: '0', | ||
}, | ||
feComposite: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'in2', | ||
'operator', | ||
'k1', | ||
'k2', | ||
'k3', | ||
'k4' | ||
], | ||
defaults: { | ||
operator: 'over', | ||
k1: '0', | ||
k2: '0', | ||
k3: '0', | ||
k4: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
fePointLight: { | ||
attrsGroups: ['core'], | ||
attrs: ['x', 'y', 'z'], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
z: '0', | ||
}, | ||
feConvolveMatrix: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'order', | ||
'kernelMatrix', | ||
// TODO: divisor - 'The default value is the sum of all values in kernelMatrix, | ||
// with the exception that if the sum is zero, then the divisor is set to 1' | ||
'divisor', | ||
'bias', | ||
// TODO: targetX - 'By default, the convolution matrix is centered in X over each | ||
// pixel of the input image (i.e., targetX = floor ( orderX / 2 ))' | ||
'targetX', | ||
'targetY', | ||
'edgeMode', | ||
// TODO: kernelUnitLength - 'The first number is the <dx> value. The second number | ||
// is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>' | ||
'kernelUnitLength', | ||
'preserveAlpha' | ||
], | ||
defaults: { | ||
order: '3', | ||
bias: '0', | ||
edgeMode: 'duplicate', | ||
preserveAlpha: 'false' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feSpecularLighting: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'surfaceScale', | ||
'specularConstant', | ||
'specularExponent', | ||
'kernelUnitLength', | ||
], | ||
defaults: { | ||
surfaceScale: '1', | ||
specularConstant: '1', | ||
specularExponent: '1', | ||
}, | ||
feDiffuseLighting: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'surfaceScale', | ||
'diffuseConstant', | ||
'kernelUnitLength' | ||
], | ||
defaults: { | ||
surfaceScale: '1', | ||
diffuseConstant: '1' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
// TODO: 'exactly one light source element, in any order' | ||
'feDistantLight', | ||
'fePointLight', | ||
'feSpotLight' | ||
] | ||
contentGroups: [ | ||
'descriptive', | ||
// TODO: exactly one 'light source element' | ||
'lightSource', | ||
], | ||
}, | ||
feSpotLight: { | ||
attrsGroups: ['core'], | ||
attrs: [ | ||
'x', | ||
'y', | ||
'z', | ||
'pointsAtX', | ||
'pointsAtY', | ||
'pointsAtZ', | ||
'specularExponent', | ||
'limitingConeAngle', | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
z: '0', | ||
pointsAtX: '0', | ||
pointsAtY: '0', | ||
pointsAtZ: '0', | ||
specularExponent: '1', | ||
}, | ||
feDisplacementMap: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'in2', | ||
'scale', | ||
'xChannelSelector', | ||
'yChannelSelector' | ||
], | ||
defaults: { | ||
scale: '0', | ||
xChannelSelector: 'A', | ||
yChannelSelector: 'A' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
feTile: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: ['class', 'style', 'in'], | ||
content: ['animate', 'set'], | ||
}, | ||
feTurbulence: { | ||
attrsGroups: ['core', 'presentation', 'filterPrimitive'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'baseFrequency', | ||
'numOctaves', | ||
'seed', | ||
'stitchTiles', | ||
'type', | ||
], | ||
defaults: { | ||
baseFrequency: '0', | ||
numOctaves: '1', | ||
seed: '0', | ||
stitchTiles: 'noStitch', | ||
type: 'turbulence', | ||
}, | ||
feDistantLight: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'azimuth', | ||
'elevation' | ||
], | ||
defaults: { | ||
azimuth: '0', | ||
elevation: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
content: ['animate', 'set'], | ||
}, | ||
filter: { | ||
attrsGroups: ['core', 'presentation', 'xlink'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'filterRes', | ||
'filterUnits', | ||
'primitiveUnits', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
primitiveUnits: 'userSpaceOnUse', | ||
x: '-10%', | ||
y: '-10%', | ||
width: '120%', | ||
height: '120%', | ||
}, | ||
feFlood: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
], | ||
content: [ | ||
'animate', | ||
'animateColor', | ||
'set' | ||
] | ||
contentGroups: ['descriptive', 'filterPrimitive'], | ||
content: ['animate', 'set'], | ||
}, | ||
font: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'horiz-origin-x', | ||
'horiz-origin-y', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y', | ||
], | ||
defaults: { | ||
'horiz-origin-x': '0', | ||
'horiz-origin-y': '0', | ||
}, | ||
feFuncA: { | ||
attrsGroups: [ | ||
'core', | ||
'transferFunction' | ||
], | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: ['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern'], | ||
}, | ||
'font-face': { | ||
attrsGroups: ['core'], | ||
attrs: [ | ||
'font-family', | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'font-stretch', | ||
'font-size', | ||
'unicode-range', | ||
'units-per-em', | ||
'panose-1', | ||
'stemv', | ||
'stemh', | ||
'slope', | ||
'cap-height', | ||
'x-height', | ||
'accent-height', | ||
'ascent', | ||
'descent', | ||
'widths', | ||
'bbox', | ||
'ideographic', | ||
'alphabetic', | ||
'mathematical', | ||
'hanging', | ||
'v-ideographic', | ||
'v-alphabetic', | ||
'v-mathematical', | ||
'v-hanging', | ||
'underline-position', | ||
'underline-thickness', | ||
'strikethrough-position', | ||
'strikethrough-thickness', | ||
'overline-position', | ||
'overline-thickness', | ||
], | ||
defaults: { | ||
'font-style': 'all', | ||
'font-variant': 'normal', | ||
'font-weight': 'all', | ||
'font-stretch': 'normal', | ||
'unicode-range': 'U+0-10FFFF', | ||
'units-per-em': '1000', | ||
'panose-1': '0 0 0 0 0 0 0 0 0 0', | ||
slope: '0', | ||
}, | ||
feFuncB: { | ||
attrsGroups: [ | ||
'core', | ||
'transferFunction' | ||
], | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: [ | ||
// TODO: "at most one 'font-face-src' element" | ||
'font-face-src', | ||
], | ||
}, | ||
// TODO: empty content | ||
'font-face-format': { | ||
attrsGroups: ['core'], | ||
attrs: ['string'], | ||
}, | ||
'font-face-name': { | ||
attrsGroups: ['core'], | ||
attrs: ['name'], | ||
}, | ||
'font-face-src': { | ||
attrsGroups: ['core'], | ||
content: ['font-face-name', 'font-face-uri'], | ||
}, | ||
'font-face-uri': { | ||
attrsGroups: ['core', 'xlink'], | ||
attrs: ['href', 'xlink:href'], | ||
content: ['font-face-format'], | ||
}, | ||
foreignObject: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
], | ||
defaults: { | ||
x: 0, | ||
y: 0, | ||
}, | ||
feFuncG: { | ||
attrsGroups: [ | ||
'core', | ||
'transferFunction' | ||
], | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
}, | ||
g: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: ['class', 'style', 'externalResourcesRequired', 'transform'], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
glyph: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y', | ||
'unicode', | ||
'glyph-name', | ||
'orientation', | ||
'arabic-form', | ||
'lang', | ||
], | ||
defaults: { | ||
'arabic-form': 'initial', | ||
}, | ||
feFuncR: { | ||
attrsGroups: [ | ||
'core', | ||
'transferFunction' | ||
], | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
glyphRef: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y', | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
hatch: { | ||
attrsGroups: ['core', 'presentation', 'xlink'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'x', | ||
'y', | ||
'pitch', | ||
'rotate', | ||
'hatchUnits', | ||
'hatchContentUnits', | ||
'transform', | ||
], | ||
defaults: { | ||
hatchUnits: 'objectBoundingBox', | ||
hatchContentUnits: 'userSpaceOnUse', | ||
x: '0', | ||
y: '0', | ||
pitch: '0', | ||
rotate: '0', | ||
}, | ||
feGaussianBlur: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'stdDeviation' | ||
], | ||
defaults: { | ||
stdDeviation: '0' | ||
}, | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
content: ['hatchPath'], | ||
}, | ||
hatchPath: { | ||
attrsGroups: ['core', 'presentation', 'xlink'], | ||
attrs: ['class', 'style', 'd', 'offset'], | ||
defaults: { | ||
offset: '0', | ||
}, | ||
feImage: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
preserveAspectRatio: 'xMidYMid meet' | ||
}, | ||
content: [ | ||
'animate', | ||
'animateTransform', | ||
'set' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
hkern: { | ||
attrsGroups: ['core'], | ||
attrs: ['u1', 'g1', 'u2', 'g2', 'k'], | ||
}, | ||
image: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'graphicalEvent', | ||
'xlink', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
preserveAspectRatio: 'xMidYMid meet', | ||
}, | ||
feMerge: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
], | ||
content: [ | ||
'feMergeNode' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
line: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x1', | ||
'y1', | ||
'x2', | ||
'y2', | ||
], | ||
defaults: { | ||
x1: '0', | ||
y1: '0', | ||
x2: '0', | ||
y2: '0', | ||
}, | ||
feMergeNode: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'in' | ||
], | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
linearGradient: { | ||
attrsGroups: ['core', 'presentation', 'xlink'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x1', | ||
'y1', | ||
'x2', | ||
'y2', | ||
'gradientUnits', | ||
'gradientTransform', | ||
'spreadMethod', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
x1: '0', | ||
y1: '0', | ||
x2: '100%', | ||
y2: '0', | ||
spreadMethod: 'pad', | ||
}, | ||
feMorphology: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'operator', | ||
'radius' | ||
], | ||
defaults: { | ||
operator: 'erode', | ||
radius: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: ['animate', 'animateTransform', 'set', 'stop'], | ||
}, | ||
marker: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'refX', | ||
'refY', | ||
'markerUnits', | ||
'markerWidth', | ||
'markerHeight', | ||
'orient', | ||
], | ||
defaults: { | ||
markerUnits: 'strokeWidth', | ||
refX: '0', | ||
refY: '0', | ||
markerWidth: '3', | ||
markerHeight: '3', | ||
}, | ||
feOffset: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'dx', | ||
'dy' | ||
], | ||
defaults: { | ||
dx: '0', | ||
dy: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
mask: { | ||
attrsGroups: ['conditionalProcessing', 'core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'mask-type', | ||
'maskUnits', | ||
'maskContentUnits', | ||
], | ||
defaults: { | ||
maskUnits: 'objectBoundingBox', | ||
maskContentUnits: 'userSpaceOnUse', | ||
x: '-10%', | ||
y: '-10%', | ||
width: '120%', | ||
height: '120%', | ||
}, | ||
fePointLight: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'x', | ||
'y', | ||
'z' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
z: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
metadata: { | ||
attrsGroups: ['core'], | ||
}, | ||
'missing-glyph': { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y', | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
mpath: { | ||
attrsGroups: ['core', 'xlink'], | ||
attrs: ['externalResourcesRequired', 'href', 'xlink:href'], | ||
contentGroups: ['descriptive'], | ||
}, | ||
path: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'd', | ||
'pathLength', | ||
], | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
pattern: { | ||
attrsGroups: ['conditionalProcessing', 'core', 'presentation', 'xlink'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'patternUnits', | ||
'patternContentUnits', | ||
'patternTransform', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
patternUnits: 'objectBoundingBox', | ||
patternContentUnits: 'userSpaceOnUse', | ||
x: '0', | ||
y: '0', | ||
width: '0', | ||
height: '0', | ||
preserveAspectRatio: 'xMidYMid meet', | ||
}, | ||
feSpecularLighting: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in', | ||
'surfaceScale', | ||
'specularConstant', | ||
'specularExponent', | ||
'kernelUnitLength' | ||
], | ||
defaults: { | ||
surfaceScale: '1', | ||
specularConstant: '1', | ||
specularExponent: '1' | ||
}, | ||
contentGroups: [ | ||
'descriptive', | ||
// TODO: exactly one 'light source element' | ||
'lightSource' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'paintServer', | ||
'shape', | ||
'structural', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
polygon: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'points', | ||
], | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
polyline: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'points', | ||
], | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
radialGradient: { | ||
attrsGroups: ['core', 'presentation', 'xlink'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'cx', | ||
'cy', | ||
'r', | ||
'fx', | ||
'fy', | ||
'fr', | ||
'gradientUnits', | ||
'gradientTransform', | ||
'spreadMethod', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
gradientUnits: 'objectBoundingBox', | ||
cx: '50%', | ||
cy: '50%', | ||
r: '50%', | ||
}, | ||
feSpotLight: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'x', | ||
'y', | ||
'z', | ||
'pointsAtX', | ||
'pointsAtY', | ||
'pointsAtZ', | ||
'specularExponent', | ||
'limitingConeAngle' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
z: '0', | ||
pointsAtX: '0', | ||
pointsAtY: '0', | ||
pointsAtZ: '0', | ||
specularExponent: '1' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: ['animate', 'animateTransform', 'set', 'stop'], | ||
}, | ||
meshGradient: { | ||
attrsGroups: ['core', 'presentation', 'xlink'], | ||
attrs: ['class', 'style', 'x', 'y', 'gradientUnits', 'transform'], | ||
contentGroups: ['descriptive', 'paintServer', 'animation'], | ||
content: ['meshRow'], | ||
}, | ||
meshRow: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: ['class', 'style'], | ||
contentGroups: ['descriptive'], | ||
content: ['meshPatch'], | ||
}, | ||
meshPatch: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: ['class', 'style'], | ||
contentGroups: ['descriptive'], | ||
content: ['stop'], | ||
}, | ||
rect: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'rx', | ||
'ry', | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
}, | ||
feTile: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'in' | ||
], | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
script: { | ||
attrsGroups: ['core', 'xlink'], | ||
attrs: ['externalResourcesRequired', 'type', 'href', 'xlink:href'], | ||
}, | ||
set: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animation', | ||
'xlink', | ||
'animationAttributeTarget', | ||
'animationTiming', | ||
], | ||
attrs: ['externalResourcesRequired', 'to'], | ||
contentGroups: ['descriptive'], | ||
}, | ||
solidColor: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: ['class', 'style'], | ||
contentGroups: ['paintServer'], | ||
}, | ||
stop: { | ||
attrsGroups: ['core', 'presentation'], | ||
attrs: ['class', 'style', 'offset', 'path'], | ||
content: ['animate', 'animateColor', 'set'], | ||
}, | ||
style: { | ||
attrsGroups: ['core'], | ||
attrs: ['type', 'media', 'title'], | ||
defaults: { | ||
type: 'text/css', | ||
}, | ||
feTurbulence: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'filterPrimitive' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'baseFrequency', | ||
'numOctaves', | ||
'seed', | ||
'stitchTiles', | ||
'type' | ||
], | ||
defaults: { | ||
baseFrequency: '0', | ||
numOctaves: '1', | ||
seed: '0', | ||
stitchTiles: 'noStitch', | ||
type: 'turbulence' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
svg: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'documentEvent', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'zoomAndPan', | ||
'version', | ||
'baseProfile', | ||
'contentScriptType', | ||
'contentStyleType', | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
width: '100%', | ||
height: '100%', | ||
preserveAspectRatio: 'xMidYMid meet', | ||
zoomAndPan: 'magnify', | ||
version: '1.1', | ||
baseProfile: 'none', | ||
contentScriptType: 'application/ecmascript', | ||
contentStyleType: 'text/css', | ||
}, | ||
filter: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'filterRes', | ||
'filterUnits', | ||
'primitiveUnits', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
primitiveUnits: 'userSpaceOnUse', | ||
x: '-10%', | ||
y: '-10%', | ||
width: '120%', | ||
height: '120%' | ||
}, | ||
contentGroups: [ | ||
'descriptive', | ||
'filterPrimitive' | ||
], | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
switch: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: ['class', 'style', 'externalResourcesRequired', 'transform'], | ||
contentGroups: ['animation', 'descriptive', 'shape'], | ||
content: [ | ||
'a', | ||
'foreignObject', | ||
'g', | ||
'image', | ||
'svg', | ||
'switch', | ||
'text', | ||
'use', | ||
], | ||
}, | ||
symbol: { | ||
attrsGroups: ['core', 'graphicalEvent', 'presentation'], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'viewBox', | ||
'refX', | ||
'refY', | ||
], | ||
defaults: { | ||
refX: 0, | ||
refY: 0, | ||
}, | ||
font: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'horiz-origin-x', | ||
'horiz-origin-y', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y' | ||
], | ||
defaults: { | ||
'horiz-origin-x': '0', | ||
'horiz-origin-y': '0' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'font-face', | ||
'glyph', | ||
'hkern', | ||
'missing-glyph', | ||
'vkern' | ||
] | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer', | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view', | ||
], | ||
}, | ||
text: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'lengthAdjust', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'rotate', | ||
'textLength', | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
lengthAdjust: 'spacing', | ||
}, | ||
'font-face': { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'font-family', | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'font-stretch', | ||
'font-size', | ||
'unicode-range', | ||
'units-per-em', | ||
'panose-1', | ||
'stemv', | ||
'stemh', | ||
'slope', | ||
'cap-height', | ||
'x-height', | ||
'accent-height', | ||
'ascent', | ||
'descent', | ||
'widths', | ||
'bbox', | ||
'ideographic', | ||
'alphabetic', | ||
'mathematical', | ||
'hanging', | ||
'v-ideographic', | ||
'v-alphabetic', | ||
'v-mathematical', | ||
'v-hanging', | ||
'underline-position', | ||
'underline-thickness', | ||
'strikethrough-position', | ||
'strikethrough-thickness', | ||
'overline-position', | ||
'overline-thickness' | ||
], | ||
defaults: { | ||
'font-style': 'all', | ||
'font-variant': 'normal', | ||
'font-weight': 'all', | ||
'font-stretch': 'normal', | ||
'unicode-range': 'U+0-10FFFF', | ||
'units-per-em': '1000', | ||
'panose-1': '0 0 0 0 0 0 0 0 0 0', | ||
'slope': '0' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
// TODO: "at most one 'font-face-src' element" | ||
'font-face-src' | ||
] | ||
contentGroups: ['animation', 'descriptive', 'textContentChild'], | ||
content: ['a'], | ||
}, | ||
textPath: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'href', | ||
'xlink:href', | ||
'startOffset', | ||
'method', | ||
'spacing', | ||
'd', | ||
], | ||
defaults: { | ||
startOffset: '0', | ||
method: 'align', | ||
spacing: 'exact', | ||
}, | ||
// TODO: empty content | ||
'font-face-format': { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'string' | ||
] | ||
contentGroups: ['descriptive'], | ||
content: [ | ||
'a', | ||
'altGlyph', | ||
'animate', | ||
'animateColor', | ||
'set', | ||
'tref', | ||
'tspan', | ||
], | ||
}, | ||
title: { | ||
attrsGroups: ['core'], | ||
attrs: ['class', 'style'], | ||
}, | ||
tref: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'href', | ||
'xlink:href', | ||
], | ||
contentGroups: ['descriptive'], | ||
content: ['animate', 'animateColor', 'set'], | ||
}, | ||
tspan: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'rotate', | ||
'textLength', | ||
'lengthAdjust', | ||
], | ||
contentGroups: ['descriptive'], | ||
content: [ | ||
'a', | ||
'altGlyph', | ||
'animate', | ||
'animateColor', | ||
'set', | ||
'tref', | ||
'tspan', | ||
], | ||
}, | ||
use: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink', | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'href', | ||
'xlink:href', | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
}, | ||
'font-face-name': { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'name' | ||
] | ||
}, | ||
'font-face-src': { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
content: [ | ||
'font-face-name', | ||
'font-face-uri' | ||
] | ||
}, | ||
'font-face-uri': { | ||
attrsGroups: [ | ||
'core', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'href', | ||
'xlink:href' | ||
], | ||
content: [ | ||
'font-face-format' | ||
] | ||
}, | ||
foreignObject: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height' | ||
], | ||
defaults: { | ||
x: 0, | ||
y: 0 | ||
} | ||
}, | ||
g: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
glyph: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y', | ||
'unicode', | ||
'glyph-name', | ||
'orientation', | ||
'arabic-form', | ||
'lang' | ||
], | ||
defaults: { | ||
'arabic-form': 'initial' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
], | ||
}, | ||
glyphRef: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
hatch: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'x', | ||
'y', | ||
'pitch', | ||
'rotate', | ||
'hatchUnits', | ||
'hatchContentUnits', | ||
'transform' | ||
], | ||
defaults: { | ||
hatchUnits: 'objectBoundingBox', | ||
hatchContentUnits: 'userSpaceOnUse', | ||
x: '0', | ||
y: '0', | ||
pitch: '0', | ||
rotate: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
], | ||
content: [ | ||
'hatchPath' | ||
] | ||
}, | ||
hatchPath: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'offset' | ||
], | ||
defaults: { | ||
offset: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
hkern: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'u1', | ||
'g1', | ||
'u2', | ||
'g2', | ||
'k' | ||
] | ||
}, | ||
image: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'graphicalEvent', | ||
'xlink', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
preserveAspectRatio: 'xMidYMid meet' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
line: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x1', | ||
'y1', | ||
'x2', | ||
'y2' | ||
], | ||
defaults: { | ||
x1: '0', | ||
y1: '0', | ||
x2: '0', | ||
y2: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
linearGradient: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x1', | ||
'y1', | ||
'x2', | ||
'y2', | ||
'gradientUnits', | ||
'gradientTransform', | ||
'spreadMethod', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
x1: '0', | ||
y1: '0', | ||
x2: '100%', | ||
y2: '0', | ||
spreadMethod: 'pad' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'animate', | ||
'animateTransform', | ||
'set', | ||
'stop' | ||
] | ||
}, | ||
marker: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'refX', | ||
'refY', | ||
'markerUnits', | ||
'markerWidth', | ||
'markerHeight', | ||
'orient' | ||
], | ||
defaults: { | ||
markerUnits: 'strokeWidth', | ||
refX: '0', | ||
refY: '0', | ||
markerWidth: '3', | ||
markerHeight: '3' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
mask: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'mask-type', | ||
'maskUnits', | ||
'maskContentUnits' | ||
], | ||
defaults: { | ||
maskUnits: 'objectBoundingBox', | ||
maskContentUnits: 'userSpaceOnUse', | ||
x: '-10%', | ||
y: '-10%', | ||
width: '120%', | ||
height: '120%' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
metadata: { | ||
attrsGroups: [ | ||
'core' | ||
] | ||
}, | ||
'missing-glyph': { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'd', | ||
'horiz-adv-x', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
'vert-adv-y' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
mpath: { | ||
attrsGroups: [ | ||
'core', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'href', | ||
'xlink:href' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
}, | ||
path: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'd', | ||
'pathLength' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
pattern: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'patternUnits', | ||
'patternContentUnits', | ||
'patternTransform', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
patternUnits: 'objectBoundingBox', | ||
patternContentUnits: 'userSpaceOnUse', | ||
x: '0', | ||
y: '0', | ||
width: '0', | ||
height: '0', | ||
preserveAspectRatio: 'xMidYMid meet' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'paintServer', | ||
'shape', | ||
'structural' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
polygon: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'points' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
polyline: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'points' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
radialGradient: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'cx', | ||
'cy', | ||
'r', | ||
'fx', | ||
'fy', | ||
'fr', | ||
'gradientUnits', | ||
'gradientTransform', | ||
'spreadMethod', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
gradientUnits: 'objectBoundingBox', | ||
cx: '50%', | ||
cy: '50%', | ||
r: '50%' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'animate', | ||
'animateTransform', | ||
'set', | ||
'stop' | ||
] | ||
}, | ||
meshGradient: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'x', | ||
'y', | ||
'gradientUnits', | ||
'transform' | ||
], | ||
contentGroups: [ | ||
'descriptive', | ||
'paintServer', | ||
'animation', | ||
], | ||
content: [ | ||
'meshRow' | ||
] | ||
}, | ||
meshRow: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'meshPatch' | ||
] | ||
}, | ||
meshPatch: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'stop' | ||
] | ||
}, | ||
rect: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'rx', | ||
'ry' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
script: { | ||
attrsGroups: [ | ||
'core', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'type', | ||
'href', | ||
'xlink:href' | ||
] | ||
}, | ||
set: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'animation', | ||
'xlink', | ||
'animationAttributeTarget', | ||
'animationTiming', | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'to' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
}, | ||
solidColor: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
], | ||
contentGroups: [ | ||
'paintServer' | ||
] | ||
}, | ||
stop: { | ||
attrsGroups: [ | ||
'core', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'offset', | ||
'path' | ||
], | ||
content: [ | ||
'animate', | ||
'animateColor', | ||
'set' | ||
] | ||
}, | ||
style: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'type', | ||
'media', | ||
'title' | ||
], | ||
defaults: { | ||
type: 'text/css' | ||
} | ||
}, | ||
svg: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'documentEvent', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'zoomAndPan', | ||
'version', | ||
'baseProfile', | ||
'contentScriptType', | ||
'contentStyleType' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
width: '100%', | ||
height: '100%', | ||
preserveAspectRatio: 'xMidYMid meet', | ||
zoomAndPan: 'magnify', | ||
version: '1.1', | ||
baseProfile: 'none', | ||
contentScriptType: 'application/ecmascript', | ||
contentStyleType: 'text/css' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
switch: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform' | ||
], | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape' | ||
], | ||
content: [ | ||
'a', | ||
'foreignObject', | ||
'g', | ||
'image', | ||
'svg', | ||
'switch', | ||
'text', | ||
'use' | ||
] | ||
}, | ||
symbol: { | ||
attrsGroups: [ | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'viewBox', | ||
'refX', | ||
'refY' | ||
], | ||
defaults: { | ||
refX: 0, | ||
refY: 0 | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'shape', | ||
'structural', | ||
'paintServer' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
text: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'lengthAdjust', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'rotate', | ||
'textLength' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
lengthAdjust: 'spacing' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive', | ||
'textContentChild' | ||
], | ||
content: [ | ||
'a' | ||
] | ||
}, | ||
textPath: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'href', | ||
'xlink:href', | ||
'startOffset', | ||
'method', | ||
'spacing', | ||
'd' | ||
], | ||
defaults: { | ||
startOffset: '0', | ||
method: 'align', | ||
spacing: 'exact' | ||
}, | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyph', | ||
'animate', | ||
'animateColor', | ||
'set', | ||
'tref', | ||
'tspan' | ||
] | ||
}, | ||
title: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'class', | ||
'style' | ||
] | ||
}, | ||
tref: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'href', | ||
'xlink:href' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'animate', | ||
'animateColor', | ||
'set' | ||
] | ||
}, | ||
tspan: { | ||
attrsGroups: [ | ||
'conditionalProcessing', | ||
'core', | ||
'graphicalEvent', | ||
'presentation' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'rotate', | ||
'textLength', | ||
'lengthAdjust' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
], | ||
content: [ | ||
'a', | ||
'altGlyph', | ||
'animate', | ||
'animateColor', | ||
'set', | ||
'tref', | ||
'tspan' | ||
] | ||
}, | ||
use: { | ||
attrsGroups: [ | ||
'core', | ||
'conditionalProcessing', | ||
'graphicalEvent', | ||
'presentation', | ||
'xlink' | ||
], | ||
attrs: [ | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'href', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0' | ||
}, | ||
contentGroups: [ | ||
'animation', | ||
'descriptive' | ||
] | ||
}, | ||
view: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'zoomAndPan', | ||
'viewTarget' | ||
], | ||
contentGroups: [ | ||
'descriptive' | ||
] | ||
}, | ||
vkern: { | ||
attrsGroups: [ | ||
'core' | ||
], | ||
attrs: [ | ||
'u1', | ||
'g1', | ||
'u2', | ||
'g2', | ||
'k' | ||
] | ||
} | ||
contentGroups: ['animation', 'descriptive'], | ||
}, | ||
view: { | ||
attrsGroups: ['core'], | ||
attrs: [ | ||
'externalResourcesRequired', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'zoomAndPan', | ||
'viewTarget', | ||
], | ||
contentGroups: ['descriptive'], | ||
}, | ||
vkern: { | ||
attrsGroups: ['core'], | ||
attrs: ['u1', 'g1', 'u2', 'g2', 'k'], | ||
}, | ||
}; | ||
@@ -2277,24 +1845,24 @@ | ||
exports.editorNamespaces = [ | ||
'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', | ||
'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd', | ||
'http://www.inkscape.org/namespaces/inkscape', | ||
'http://www.bohemiancoding.com/sketch/ns', | ||
'http://ns.adobe.com/AdobeIllustrator/10.0/', | ||
'http://ns.adobe.com/Graphs/1.0/', | ||
'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/', | ||
'http://ns.adobe.com/Variables/1.0/', | ||
'http://ns.adobe.com/SaveForWeb/1.0/', | ||
'http://ns.adobe.com/Extensibility/1.0/', | ||
'http://ns.adobe.com/Flows/1.0/', | ||
'http://ns.adobe.com/ImageReplacement/1.0/', | ||
'http://ns.adobe.com/GenericCustomNamespace/1.0/', | ||
'http://ns.adobe.com/XPath/1.0/', | ||
'http://schemas.microsoft.com/visio/2003/SVGExtensions/', | ||
'http://taptrix.com/vectorillustrator/svg_extensions', | ||
'http://www.figma.com/figma/ns', | ||
'http://purl.org/dc/elements/1.1/', | ||
'http://creativecommons.org/ns#', | ||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | ||
'http://www.serif.com/', | ||
'http://www.vector.evaxdesign.sk' | ||
'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', | ||
'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd', | ||
'http://www.inkscape.org/namespaces/inkscape', | ||
'http://www.bohemiancoding.com/sketch/ns', | ||
'http://ns.adobe.com/AdobeIllustrator/10.0/', | ||
'http://ns.adobe.com/Graphs/1.0/', | ||
'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/', | ||
'http://ns.adobe.com/Variables/1.0/', | ||
'http://ns.adobe.com/SaveForWeb/1.0/', | ||
'http://ns.adobe.com/Extensibility/1.0/', | ||
'http://ns.adobe.com/Flows/1.0/', | ||
'http://ns.adobe.com/ImageReplacement/1.0/', | ||
'http://ns.adobe.com/GenericCustomNamespace/1.0/', | ||
'http://ns.adobe.com/XPath/1.0/', | ||
'http://schemas.microsoft.com/visio/2003/SVGExtensions/', | ||
'http://taptrix.com/vectorillustrator/svg_extensions', | ||
'http://www.figma.com/figma/ns', | ||
'http://purl.org/dc/elements/1.1/', | ||
'http://creativecommons.org/ns#', | ||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | ||
'http://www.serif.com/', | ||
'http://www.vector.evaxdesign.sk', | ||
]; | ||
@@ -2304,12 +1872,12 @@ | ||
exports.referencesProps = [ | ||
'clip-path', | ||
'color-profile', | ||
'fill', | ||
'filter', | ||
'marker-start', | ||
'marker-mid', | ||
'marker-end', | ||
'mask', | ||
'stroke', | ||
'style' | ||
'clip-path', | ||
'color-profile', | ||
'fill', | ||
'filter', | ||
'marker-start', | ||
'marker-mid', | ||
'marker-end', | ||
'mask', | ||
'stroke', | ||
'style', | ||
]; | ||
@@ -2319,59 +1887,59 @@ | ||
exports.inheritableAttrs = [ | ||
'clip-rule', | ||
'color', | ||
'color-interpolation', | ||
'color-interpolation-filters', | ||
'color-profile', | ||
'color-rendering', | ||
'cursor', | ||
'direction', | ||
'dominant-baseline', | ||
'fill', | ||
'fill-opacity', | ||
'fill-rule', | ||
'font', | ||
'font-family', | ||
'font-size', | ||
'font-size-adjust', | ||
'font-stretch', | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'glyph-orientation-horizontal', | ||
'glyph-orientation-vertical', | ||
'image-rendering', | ||
'letter-spacing', | ||
'marker', | ||
'marker-end', | ||
'marker-mid', | ||
'marker-start', | ||
'paint-order', | ||
'pointer-events', | ||
'shape-rendering', | ||
'stroke', | ||
'stroke-dasharray', | ||
'stroke-dashoffset', | ||
'stroke-linecap', | ||
'stroke-linejoin', | ||
'stroke-miterlimit', | ||
'stroke-opacity', | ||
'stroke-width', | ||
'text-anchor', | ||
'text-rendering', | ||
'transform', | ||
'visibility', | ||
'word-spacing', | ||
'writing-mode' | ||
'clip-rule', | ||
'color', | ||
'color-interpolation', | ||
'color-interpolation-filters', | ||
'color-profile', | ||
'color-rendering', | ||
'cursor', | ||
'direction', | ||
'dominant-baseline', | ||
'fill', | ||
'fill-opacity', | ||
'fill-rule', | ||
'font', | ||
'font-family', | ||
'font-size', | ||
'font-size-adjust', | ||
'font-stretch', | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'glyph-orientation-horizontal', | ||
'glyph-orientation-vertical', | ||
'image-rendering', | ||
'letter-spacing', | ||
'marker', | ||
'marker-end', | ||
'marker-mid', | ||
'marker-start', | ||
'paint-order', | ||
'pointer-events', | ||
'shape-rendering', | ||
'stroke', | ||
'stroke-dasharray', | ||
'stroke-dashoffset', | ||
'stroke-linecap', | ||
'stroke-linejoin', | ||
'stroke-miterlimit', | ||
'stroke-opacity', | ||
'stroke-width', | ||
'text-anchor', | ||
'text-rendering', | ||
'transform', | ||
'visibility', | ||
'word-spacing', | ||
'writing-mode', | ||
]; | ||
exports.presentationNonInheritableGroupAttrs = [ | ||
'display', | ||
'clip-path', | ||
'filter', | ||
'mask', | ||
'opacity', | ||
'text-decoration', | ||
'transform', | ||
'unicode-bidi', | ||
'visibility' | ||
'display', | ||
'clip-path', | ||
'filter', | ||
'mask', | ||
'opacity', | ||
'text-decoration', | ||
'transform', | ||
'unicode-bidi', | ||
'visibility', | ||
]; | ||
@@ -2381,150 +1949,150 @@ | ||
exports.colorsNames = { | ||
'aliceblue': '#f0f8ff', | ||
'antiquewhite': '#faebd7', | ||
'aqua': '#0ff', | ||
'aquamarine': '#7fffd4', | ||
'azure': '#f0ffff', | ||
'beige': '#f5f5dc', | ||
'bisque': '#ffe4c4', | ||
'black': '#000', | ||
'blanchedalmond': '#ffebcd', | ||
'blue': '#00f', | ||
'blueviolet': '#8a2be2', | ||
'brown': '#a52a2a', | ||
'burlywood': '#deb887', | ||
'cadetblue': '#5f9ea0', | ||
'chartreuse': '#7fff00', | ||
'chocolate': '#d2691e', | ||
'coral': '#ff7f50', | ||
'cornflowerblue': '#6495ed', | ||
'cornsilk': '#fff8dc', | ||
'crimson': '#dc143c', | ||
'cyan': '#0ff', | ||
'darkblue': '#00008b', | ||
'darkcyan': '#008b8b', | ||
'darkgoldenrod': '#b8860b', | ||
'darkgray': '#a9a9a9', | ||
'darkgreen': '#006400', | ||
'darkgrey': '#a9a9a9', | ||
'darkkhaki': '#bdb76b', | ||
'darkmagenta': '#8b008b', | ||
'darkolivegreen': '#556b2f', | ||
'darkorange': '#ff8c00', | ||
'darkorchid': '#9932cc', | ||
'darkred': '#8b0000', | ||
'darksalmon': '#e9967a', | ||
'darkseagreen': '#8fbc8f', | ||
'darkslateblue': '#483d8b', | ||
'darkslategray': '#2f4f4f', | ||
'darkslategrey': '#2f4f4f', | ||
'darkturquoise': '#00ced1', | ||
'darkviolet': '#9400d3', | ||
'deeppink': '#ff1493', | ||
'deepskyblue': '#00bfff', | ||
'dimgray': '#696969', | ||
'dimgrey': '#696969', | ||
'dodgerblue': '#1e90ff', | ||
'firebrick': '#b22222', | ||
'floralwhite': '#fffaf0', | ||
'forestgreen': '#228b22', | ||
'fuchsia': '#f0f', | ||
'gainsboro': '#dcdcdc', | ||
'ghostwhite': '#f8f8ff', | ||
'gold': '#ffd700', | ||
'goldenrod': '#daa520', | ||
'gray': '#808080', | ||
'green': '#008000', | ||
'greenyellow': '#adff2f', | ||
'grey': '#808080', | ||
'honeydew': '#f0fff0', | ||
'hotpink': '#ff69b4', | ||
'indianred': '#cd5c5c', | ||
'indigo': '#4b0082', | ||
'ivory': '#fffff0', | ||
'khaki': '#f0e68c', | ||
'lavender': '#e6e6fa', | ||
'lavenderblush': '#fff0f5', | ||
'lawngreen': '#7cfc00', | ||
'lemonchiffon': '#fffacd', | ||
'lightblue': '#add8e6', | ||
'lightcoral': '#f08080', | ||
'lightcyan': '#e0ffff', | ||
'lightgoldenrodyellow': '#fafad2', | ||
'lightgray': '#d3d3d3', | ||
'lightgreen': '#90ee90', | ||
'lightgrey': '#d3d3d3', | ||
'lightpink': '#ffb6c1', | ||
'lightsalmon': '#ffa07a', | ||
'lightseagreen': '#20b2aa', | ||
'lightskyblue': '#87cefa', | ||
'lightslategray': '#789', | ||
'lightslategrey': '#789', | ||
'lightsteelblue': '#b0c4de', | ||
'lightyellow': '#ffffe0', | ||
'lime': '#0f0', | ||
'limegreen': '#32cd32', | ||
'linen': '#faf0e6', | ||
'magenta': '#f0f', | ||
'maroon': '#800000', | ||
'mediumaquamarine': '#66cdaa', | ||
'mediumblue': '#0000cd', | ||
'mediumorchid': '#ba55d3', | ||
'mediumpurple': '#9370db', | ||
'mediumseagreen': '#3cb371', | ||
'mediumslateblue': '#7b68ee', | ||
'mediumspringgreen': '#00fa9a', | ||
'mediumturquoise': '#48d1cc', | ||
'mediumvioletred': '#c71585', | ||
'midnightblue': '#191970', | ||
'mintcream': '#f5fffa', | ||
'mistyrose': '#ffe4e1', | ||
'moccasin': '#ffe4b5', | ||
'navajowhite': '#ffdead', | ||
'navy': '#000080', | ||
'oldlace': '#fdf5e6', | ||
'olive': '#808000', | ||
'olivedrab': '#6b8e23', | ||
'orange': '#ffa500', | ||
'orangered': '#ff4500', | ||
'orchid': '#da70d6', | ||
'palegoldenrod': '#eee8aa', | ||
'palegreen': '#98fb98', | ||
'paleturquoise': '#afeeee', | ||
'palevioletred': '#db7093', | ||
'papayawhip': '#ffefd5', | ||
'peachpuff': '#ffdab9', | ||
'peru': '#cd853f', | ||
'pink': '#ffc0cb', | ||
'plum': '#dda0dd', | ||
'powderblue': '#b0e0e6', | ||
'purple': '#800080', | ||
'rebeccapurple': '#639', | ||
'red': '#f00', | ||
'rosybrown': '#bc8f8f', | ||
'royalblue': '#4169e1', | ||
'saddlebrown': '#8b4513', | ||
'salmon': '#fa8072', | ||
'sandybrown': '#f4a460', | ||
'seagreen': '#2e8b57', | ||
'seashell': '#fff5ee', | ||
'sienna': '#a0522d', | ||
'silver': '#c0c0c0', | ||
'skyblue': '#87ceeb', | ||
'slateblue': '#6a5acd', | ||
'slategray': '#708090', | ||
'slategrey': '#708090', | ||
'snow': '#fffafa', | ||
'springgreen': '#00ff7f', | ||
'steelblue': '#4682b4', | ||
'tan': '#d2b48c', | ||
'teal': '#008080', | ||
'thistle': '#d8bfd8', | ||
'tomato': '#ff6347', | ||
'turquoise': '#40e0d0', | ||
'violet': '#ee82ee', | ||
'wheat': '#f5deb3', | ||
'white': '#fff', | ||
'whitesmoke': '#f5f5f5', | ||
'yellow': '#ff0', | ||
'yellowgreen': '#9acd32' | ||
aliceblue: '#f0f8ff', | ||
antiquewhite: '#faebd7', | ||
aqua: '#0ff', | ||
aquamarine: '#7fffd4', | ||
azure: '#f0ffff', | ||
beige: '#f5f5dc', | ||
bisque: '#ffe4c4', | ||
black: '#000', | ||
blanchedalmond: '#ffebcd', | ||
blue: '#00f', | ||
blueviolet: '#8a2be2', | ||
brown: '#a52a2a', | ||
burlywood: '#deb887', | ||
cadetblue: '#5f9ea0', | ||
chartreuse: '#7fff00', | ||
chocolate: '#d2691e', | ||
coral: '#ff7f50', | ||
cornflowerblue: '#6495ed', | ||
cornsilk: '#fff8dc', | ||
crimson: '#dc143c', | ||
cyan: '#0ff', | ||
darkblue: '#00008b', | ||
darkcyan: '#008b8b', | ||
darkgoldenrod: '#b8860b', | ||
darkgray: '#a9a9a9', | ||
darkgreen: '#006400', | ||
darkgrey: '#a9a9a9', | ||
darkkhaki: '#bdb76b', | ||
darkmagenta: '#8b008b', | ||
darkolivegreen: '#556b2f', | ||
darkorange: '#ff8c00', | ||
darkorchid: '#9932cc', | ||
darkred: '#8b0000', | ||
darksalmon: '#e9967a', | ||
darkseagreen: '#8fbc8f', | ||
darkslateblue: '#483d8b', | ||
darkslategray: '#2f4f4f', | ||
darkslategrey: '#2f4f4f', | ||
darkturquoise: '#00ced1', | ||
darkviolet: '#9400d3', | ||
deeppink: '#ff1493', | ||
deepskyblue: '#00bfff', | ||
dimgray: '#696969', | ||
dimgrey: '#696969', | ||
dodgerblue: '#1e90ff', | ||
firebrick: '#b22222', | ||
floralwhite: '#fffaf0', | ||
forestgreen: '#228b22', | ||
fuchsia: '#f0f', | ||
gainsboro: '#dcdcdc', | ||
ghostwhite: '#f8f8ff', | ||
gold: '#ffd700', | ||
goldenrod: '#daa520', | ||
gray: '#808080', | ||
green: '#008000', | ||
greenyellow: '#adff2f', | ||
grey: '#808080', | ||
honeydew: '#f0fff0', | ||
hotpink: '#ff69b4', | ||
indianred: '#cd5c5c', | ||
indigo: '#4b0082', | ||
ivory: '#fffff0', | ||
khaki: '#f0e68c', | ||
lavender: '#e6e6fa', | ||
lavenderblush: '#fff0f5', | ||
lawngreen: '#7cfc00', | ||
lemonchiffon: '#fffacd', | ||
lightblue: '#add8e6', | ||
lightcoral: '#f08080', | ||
lightcyan: '#e0ffff', | ||
lightgoldenrodyellow: '#fafad2', | ||
lightgray: '#d3d3d3', | ||
lightgreen: '#90ee90', | ||
lightgrey: '#d3d3d3', | ||
lightpink: '#ffb6c1', | ||
lightsalmon: '#ffa07a', | ||
lightseagreen: '#20b2aa', | ||
lightskyblue: '#87cefa', | ||
lightslategray: '#789', | ||
lightslategrey: '#789', | ||
lightsteelblue: '#b0c4de', | ||
lightyellow: '#ffffe0', | ||
lime: '#0f0', | ||
limegreen: '#32cd32', | ||
linen: '#faf0e6', | ||
magenta: '#f0f', | ||
maroon: '#800000', | ||
mediumaquamarine: '#66cdaa', | ||
mediumblue: '#0000cd', | ||
mediumorchid: '#ba55d3', | ||
mediumpurple: '#9370db', | ||
mediumseagreen: '#3cb371', | ||
mediumslateblue: '#7b68ee', | ||
mediumspringgreen: '#00fa9a', | ||
mediumturquoise: '#48d1cc', | ||
mediumvioletred: '#c71585', | ||
midnightblue: '#191970', | ||
mintcream: '#f5fffa', | ||
mistyrose: '#ffe4e1', | ||
moccasin: '#ffe4b5', | ||
navajowhite: '#ffdead', | ||
navy: '#000080', | ||
oldlace: '#fdf5e6', | ||
olive: '#808000', | ||
olivedrab: '#6b8e23', | ||
orange: '#ffa500', | ||
orangered: '#ff4500', | ||
orchid: '#da70d6', | ||
palegoldenrod: '#eee8aa', | ||
palegreen: '#98fb98', | ||
paleturquoise: '#afeeee', | ||
palevioletred: '#db7093', | ||
papayawhip: '#ffefd5', | ||
peachpuff: '#ffdab9', | ||
peru: '#cd853f', | ||
pink: '#ffc0cb', | ||
plum: '#dda0dd', | ||
powderblue: '#b0e0e6', | ||
purple: '#800080', | ||
rebeccapurple: '#639', | ||
red: '#f00', | ||
rosybrown: '#bc8f8f', | ||
royalblue: '#4169e1', | ||
saddlebrown: '#8b4513', | ||
salmon: '#fa8072', | ||
sandybrown: '#f4a460', | ||
seagreen: '#2e8b57', | ||
seashell: '#fff5ee', | ||
sienna: '#a0522d', | ||
silver: '#c0c0c0', | ||
skyblue: '#87ceeb', | ||
slateblue: '#6a5acd', | ||
slategray: '#708090', | ||
slategrey: '#708090', | ||
snow: '#fffafa', | ||
springgreen: '#00ff7f', | ||
steelblue: '#4682b4', | ||
tan: '#d2b48c', | ||
teal: '#008080', | ||
thistle: '#d8bfd8', | ||
tomato: '#ff6347', | ||
turquoise: '#40e0d0', | ||
violet: '#ee82ee', | ||
wheat: '#f5deb3', | ||
white: '#fff', | ||
whitesmoke: '#f5f5f5', | ||
yellow: '#ff0', | ||
yellowgreen: '#9acd32', | ||
}; | ||
@@ -2564,3 +2132,3 @@ | ||
'#ee82ee': 'violet', | ||
'#f5deb3': 'wheat' | ||
'#f5deb3': 'wheat', | ||
}; | ||
@@ -2570,3 +2138,8 @@ | ||
exports.colorsProps = [ | ||
'color', 'fill', 'stroke', 'stop-color', 'flood-color', 'lighting-color' | ||
'color', | ||
'fill', | ||
'stroke', | ||
'stop-color', | ||
'flood-color', | ||
'lighting-color', | ||
]; |
1356
plugins/_path.js
@@ -6,10 +6,11 @@ 'use strict'; | ||
var regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g, | ||
transform2js = require('./_transforms').transform2js, | ||
transformsMultiply = require('./_transforms').transformsMultiply, | ||
transformArc = require('./_transforms').transformArc, | ||
collections = require('./_collections.js'), | ||
referencesProps = collections.referencesProps, | ||
defaultStrokeWidth = collections.attrsGroupsDefaults.presentation['stroke-width'], | ||
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero, | ||
prevCtrlPoint; | ||
transform2js = require('./_transforms').transform2js, | ||
transformsMultiply = require('./_transforms').transformsMultiply, | ||
transformArc = require('./_transforms').transformArc, | ||
collections = require('./_collections.js'), | ||
referencesProps = collections.referencesProps, | ||
defaultStrokeWidth = | ||
collections.attrsGroupsDefaults.presentation['stroke-width'], | ||
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero, | ||
prevCtrlPoint; | ||
@@ -23,5 +24,5 @@ /** | ||
*/ | ||
exports.path2js = function(path) { | ||
exports.path2js = function (path) { | ||
if (path.pathJS) return path.pathJS; | ||
const pathData = []; // JS representation of the path data | ||
const pathData = []; // JS representation of the path data | ||
const newPathData = parsePathData(path.attr('d').value); | ||
@@ -49,72 +50,52 @@ for (const { command, args } of newPathData) { | ||
*/ | ||
var relative2absolute = exports.relative2absolute = function(data) { | ||
var currentPoint = [0, 0], | ||
subpathPoint = [0, 0], | ||
i; | ||
var relative2absolute = (exports.relative2absolute = function (data) { | ||
var currentPoint = [0, 0], | ||
subpathPoint = [0, 0], | ||
i; | ||
return data.map(function(item) { | ||
return data.map(function (item) { | ||
var instruction = item.instruction, | ||
itemData = item.data && item.data.slice(); | ||
var instruction = item.instruction, | ||
itemData = item.data && item.data.slice(); | ||
if (instruction == 'M') { | ||
set(currentPoint, itemData); | ||
set(subpathPoint, itemData); | ||
} else if ('mlcsqt'.indexOf(instruction) > -1) { | ||
for (i = 0; i < itemData.length; i++) { | ||
itemData[i] += currentPoint[i % 2]; | ||
} | ||
set(currentPoint, itemData); | ||
if (instruction == 'M') { | ||
if (instruction == 'm') { | ||
set(subpathPoint, itemData); | ||
} | ||
} else if (instruction == 'a') { | ||
itemData[5] += currentPoint[0]; | ||
itemData[6] += currentPoint[1]; | ||
set(currentPoint, itemData); | ||
} else if (instruction == 'h') { | ||
itemData[0] += currentPoint[0]; | ||
currentPoint[0] = itemData[0]; | ||
} else if (instruction == 'v') { | ||
itemData[0] += currentPoint[1]; | ||
currentPoint[1] = itemData[0]; | ||
} else if ('MZLCSQTA'.indexOf(instruction) > -1) { | ||
set(currentPoint, itemData); | ||
} else if (instruction == 'H') { | ||
currentPoint[0] = itemData[0]; | ||
} else if (instruction == 'V') { | ||
currentPoint[1] = itemData[0]; | ||
} else if (instruction == 'z') { | ||
set(currentPoint, subpathPoint); | ||
} | ||
set(currentPoint, itemData); | ||
set(subpathPoint, itemData); | ||
return instruction == 'z' | ||
? { instruction: 'z' } | ||
: { | ||
instruction: instruction.toUpperCase(), | ||
data: itemData, | ||
}; | ||
}); | ||
}); | ||
} else if ('mlcsqt'.indexOf(instruction) > -1) { | ||
for (i = 0; i < itemData.length; i++) { | ||
itemData[i] += currentPoint[i % 2]; | ||
} | ||
set(currentPoint, itemData); | ||
if (instruction == 'm') { | ||
set(subpathPoint, itemData); | ||
} | ||
} else if (instruction == 'a') { | ||
itemData[5] += currentPoint[0]; | ||
itemData[6] += currentPoint[1]; | ||
set(currentPoint, itemData); | ||
} else if (instruction == 'h') { | ||
itemData[0] += currentPoint[0]; | ||
currentPoint[0] = itemData[0]; | ||
} else if (instruction == 'v') { | ||
itemData[0] += currentPoint[1]; | ||
currentPoint[1] = itemData[0]; | ||
} else if ('MZLCSQTA'.indexOf(instruction) > -1) { | ||
set(currentPoint, itemData); | ||
} else if (instruction == 'H') { | ||
currentPoint[0] = itemData[0]; | ||
} else if (instruction == 'V') { | ||
currentPoint[1] = itemData[0]; | ||
} else if (instruction == 'z') { | ||
set(currentPoint, subpathPoint); | ||
} | ||
return instruction == 'z' ? | ||
{ instruction: 'z' } : | ||
{ | ||
instruction: instruction.toUpperCase(), | ||
data: itemData | ||
}; | ||
}); | ||
}; | ||
/** | ||
@@ -128,3 +109,3 @@ * Apply transformation(s) to the Path data. | ||
*/ | ||
exports.applyTransforms = function(elem, path, params) { | ||
exports.applyTransforms = function (elem, path, params) { | ||
// if there are no 'stroke' attr and references to other objects such as | ||
@@ -138,147 +119,167 @@ // gradiends or clip-path which are also subjects to transform. | ||
elem.hasAttr('style') || | ||
elem.someAttr((attr) => | ||
referencesProps.includes(attr.name) && attr.value.includes('url(') | ||
elem.someAttr( | ||
(attr) => | ||
referencesProps.includes(attr.name) && attr.value.includes('url(') | ||
) | ||
) { | ||
return path; | ||
return path; | ||
} | ||
var matrix = transformsMultiply(transform2js(elem.attr('transform').value)), | ||
stroke = elem.computedAttr('stroke'), | ||
id = elem.computedAttr('id'), | ||
transformPrecision = params.transformPrecision, | ||
newPoint, scale; | ||
var matrix = transformsMultiply(transform2js(elem.attr('transform').value)), | ||
stroke = elem.computedAttr('stroke'), | ||
id = elem.computedAttr('id'), | ||
transformPrecision = params.transformPrecision, | ||
newPoint, | ||
scale; | ||
if (stroke && stroke != 'none') { | ||
if (!params.applyTransformsStroked || | ||
(matrix.data[0] != matrix.data[3] || matrix.data[1] != -matrix.data[2]) && | ||
(matrix.data[0] != -matrix.data[3] || matrix.data[1] != matrix.data[2])) | ||
return path; | ||
if (stroke && stroke != 'none') { | ||
if ( | ||
!params.applyTransformsStroked || | ||
((matrix.data[0] != matrix.data[3] || | ||
matrix.data[1] != -matrix.data[2]) && | ||
(matrix.data[0] != -matrix.data[3] || matrix.data[1] != matrix.data[2])) | ||
) | ||
return path; | ||
// "stroke-width" should be inside the part with ID, otherwise it can be overrided in <use> | ||
if (id) { | ||
var idElem = elem, | ||
hasStrokeWidth = false; | ||
// "stroke-width" should be inside the part with ID, otherwise it can be overrided in <use> | ||
if (id) { | ||
var idElem = elem, | ||
hasStrokeWidth = false; | ||
do { | ||
if (idElem.hasAttr('stroke-width')) hasStrokeWidth = true; | ||
} while (!idElem.hasAttr('id', id) && !hasStrokeWidth && (idElem = idElem.parentNode)); | ||
do { | ||
if (idElem.hasAttr('stroke-width')) hasStrokeWidth = true; | ||
} while ( | ||
!idElem.hasAttr('id', id) && | ||
!hasStrokeWidth && | ||
(idElem = idElem.parentNode) | ||
); | ||
if (!hasStrokeWidth) return path; | ||
} | ||
if (!hasStrokeWidth) return path; | ||
} | ||
scale = +Math.sqrt(matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1]).toFixed(transformPrecision); | ||
scale = +Math.sqrt( | ||
matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1] | ||
).toFixed(transformPrecision); | ||
if (scale !== 1) { | ||
var strokeWidth = elem.computedAttr('stroke-width') || defaultStrokeWidth; | ||
if (scale !== 1) { | ||
var strokeWidth = elem.computedAttr('stroke-width') || defaultStrokeWidth; | ||
if (!elem.hasAttr('vector-effect') || elem.attr('vector-effect').value !== 'non-scaling-stroke') { | ||
if (elem.hasAttr('stroke-width')) { | ||
elem.attrs['stroke-width'].value = elem.attrs['stroke-width'].value.trim() | ||
.replace(regNumericValues, function(num) { | ||
return removeLeadingZero(num * scale); | ||
}); | ||
} else { | ||
elem.addAttr({ | ||
name: 'stroke-width', | ||
prefix: '', | ||
local: 'stroke-width', | ||
value: strokeWidth.replace(regNumericValues, function(num) { | ||
return removeLeadingZero(num * scale); | ||
}) | ||
}); | ||
} | ||
if ( | ||
!elem.hasAttr('vector-effect') || | ||
elem.attr('vector-effect').value !== 'non-scaling-stroke' | ||
) { | ||
if (elem.hasAttr('stroke-width')) { | ||
elem.attrs['stroke-width'].value = elem.attrs['stroke-width'].value | ||
.trim() | ||
.replace(regNumericValues, function (num) { | ||
return removeLeadingZero(num * scale); | ||
}); | ||
} else { | ||
elem.addAttr({ | ||
name: 'stroke-width', | ||
prefix: '', | ||
local: 'stroke-width', | ||
value: strokeWidth.replace(regNumericValues, function (num) { | ||
return removeLeadingZero(num * scale); | ||
}), | ||
}); | ||
} | ||
if (elem.hasAttr('stroke-dashoffset')) { | ||
elem.attrs['stroke-dashoffset'].value = elem.attrs['stroke-dashoffset'].value | ||
.trim() | ||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale)); | ||
} | ||
if (elem.hasAttr('stroke-dashoffset')) { | ||
elem.attrs['stroke-dashoffset'].value = elem.attrs[ | ||
'stroke-dashoffset' | ||
].value | ||
.trim() | ||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale)); | ||
} | ||
if (elem.hasAttr('stroke-dasharray')) { | ||
elem.attrs['stroke-dasharray'].value = elem.attrs['stroke-dasharray'].value | ||
.trim() | ||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale)); | ||
} | ||
} | ||
if (elem.hasAttr('stroke-dasharray')) { | ||
elem.attrs['stroke-dasharray'].value = elem.attrs[ | ||
'stroke-dasharray' | ||
].value | ||
.trim() | ||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale)); | ||
} | ||
} else if (id) { // Stroke and stroke-width can be redefined with <use> | ||
return path; | ||
} | ||
} | ||
} else if (id) { | ||
// Stroke and stroke-width can be redefined with <use> | ||
return path; | ||
} | ||
path.forEach(function(pathItem) { | ||
path.forEach(function (pathItem) { | ||
if (pathItem.data) { | ||
// h -> l | ||
if (pathItem.instruction === 'h') { | ||
pathItem.instruction = 'l'; | ||
pathItem.data[1] = 0; | ||
if (pathItem.data) { | ||
// v -> l | ||
} else if (pathItem.instruction === 'v') { | ||
pathItem.instruction = 'l'; | ||
pathItem.data[1] = pathItem.data[0]; | ||
pathItem.data[0] = 0; | ||
} | ||
// h -> l | ||
if (pathItem.instruction === 'h') { | ||
// if there is a translate() transform | ||
if ( | ||
pathItem.instruction === 'M' && | ||
(matrix.data[4] !== 0 || matrix.data[5] !== 0) | ||
) { | ||
// then apply it only to the first absoluted M | ||
newPoint = transformPoint( | ||
matrix.data, | ||
pathItem.data[0], | ||
pathItem.data[1] | ||
); | ||
set(pathItem.data, newPoint); | ||
set(pathItem.coords, newPoint); | ||
pathItem.instruction = 'l'; | ||
pathItem.data[1] = 0; | ||
// clear translate() data from transform matrix | ||
matrix.data[4] = 0; | ||
matrix.data[5] = 0; | ||
} else { | ||
if (pathItem.instruction == 'a') { | ||
transformArc(pathItem.data, matrix.data); | ||
// v -> l | ||
} else if (pathItem.instruction === 'v') { | ||
// reduce number of digits in rotation angle | ||
if (Math.abs(pathItem.data[2]) > 80) { | ||
var a = pathItem.data[0], | ||
rotation = pathItem.data[2]; | ||
pathItem.data[0] = pathItem.data[1]; | ||
pathItem.data[1] = a; | ||
pathItem.data[2] = rotation + (rotation > 0 ? -90 : 90); | ||
} | ||
pathItem.instruction = 'l'; | ||
pathItem.data[1] = pathItem.data[0]; | ||
pathItem.data[0] = 0; | ||
} | ||
// if there is a translate() transform | ||
if (pathItem.instruction === 'M' && | ||
(matrix.data[4] !== 0 || | ||
matrix.data[5] !== 0) | ||
) { | ||
// then apply it only to the first absoluted M | ||
newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]); | ||
set(pathItem.data, newPoint); | ||
set(pathItem.coords, newPoint); | ||
// clear translate() data from transform matrix | ||
matrix.data[4] = 0; | ||
matrix.data[5] = 0; | ||
} else { | ||
if (pathItem.instruction == 'a') { | ||
transformArc(pathItem.data, matrix.data); | ||
// reduce number of digits in rotation angle | ||
if (Math.abs(pathItem.data[2]) > 80) { | ||
var a = pathItem.data[0], | ||
rotation = pathItem.data[2]; | ||
pathItem.data[0] = pathItem.data[1]; | ||
pathItem.data[1] = a; | ||
pathItem.data[2] = rotation + (rotation > 0 ? -90 : 90); | ||
} | ||
newPoint = transformPoint(matrix.data, pathItem.data[5], pathItem.data[6]); | ||
pathItem.data[5] = newPoint[0]; | ||
pathItem.data[6] = newPoint[1]; | ||
} else { | ||
for (var i = 0; i < pathItem.data.length; i += 2) { | ||
newPoint = transformPoint(matrix.data, pathItem.data[i], pathItem.data[i + 1]); | ||
pathItem.data[i] = newPoint[0]; | ||
pathItem.data[i + 1] = newPoint[1]; | ||
} | ||
} | ||
pathItem.coords[0] = pathItem.base[0] + pathItem.data[pathItem.data.length - 2]; | ||
pathItem.coords[1] = pathItem.base[1] + pathItem.data[pathItem.data.length - 1]; | ||
} | ||
newPoint = transformPoint( | ||
matrix.data, | ||
pathItem.data[5], | ||
pathItem.data[6] | ||
); | ||
pathItem.data[5] = newPoint[0]; | ||
pathItem.data[6] = newPoint[1]; | ||
} else { | ||
for (var i = 0; i < pathItem.data.length; i += 2) { | ||
newPoint = transformPoint( | ||
matrix.data, | ||
pathItem.data[i], | ||
pathItem.data[i + 1] | ||
); | ||
pathItem.data[i] = newPoint[0]; | ||
pathItem.data[i + 1] = newPoint[1]; | ||
} | ||
} | ||
}); | ||
pathItem.coords[0] = | ||
pathItem.base[0] + pathItem.data[pathItem.data.length - 2]; | ||
pathItem.coords[1] = | ||
pathItem.base[1] + pathItem.data[pathItem.data.length - 1]; | ||
} | ||
} | ||
}); | ||
// remove transform attr | ||
elem.removeAttr('transform'); | ||
// remove transform attr | ||
elem.removeAttr('transform'); | ||
return path; | ||
return path; | ||
}; | ||
@@ -294,8 +295,6 @@ | ||
function transformPoint(matrix, x, y) { | ||
return [ | ||
matrix[0] * x + matrix[2] * y + matrix[4], | ||
matrix[1] * x + matrix[3] * y + matrix[5] | ||
]; | ||
return [ | ||
matrix[0] * x + matrix[2] * y + matrix[4], | ||
matrix[1] * x + matrix[3] * y + matrix[5], | ||
]; | ||
} | ||
@@ -319,65 +318,83 @@ | ||
*/ | ||
exports.computeCubicBoundingBox = function(xa, ya, xb, yb, xc, yc, xd, yd) { | ||
exports.computeCubicBoundingBox = function (xa, ya, xb, yb, xc, yc, xd, yd) { | ||
var minx = Number.POSITIVE_INFINITY, | ||
miny = Number.POSITIVE_INFINITY, | ||
maxx = Number.NEGATIVE_INFINITY, | ||
maxy = Number.NEGATIVE_INFINITY, | ||
ts, | ||
t, | ||
x, | ||
y, | ||
i; | ||
var minx = Number.POSITIVE_INFINITY, | ||
miny = Number.POSITIVE_INFINITY, | ||
maxx = Number.NEGATIVE_INFINITY, | ||
maxy = Number.NEGATIVE_INFINITY, | ||
ts, | ||
t, | ||
x, | ||
y, | ||
i; | ||
// X | ||
if (xa < minx) { | ||
minx = xa; | ||
} | ||
if (xa > maxx) { | ||
maxx = xa; | ||
} | ||
if (xd < minx) { | ||
minx = xd; | ||
} | ||
if (xd > maxx) { | ||
maxx = xd; | ||
} | ||
// X | ||
if (xa < minx) { minx = xa; } | ||
if (xa > maxx) { maxx = xa; } | ||
if (xd < minx) { minx= xd; } | ||
if (xd > maxx) { maxx = xd; } | ||
ts = computeCubicFirstDerivativeRoots(xa, xb, xc, xd); | ||
ts = computeCubicFirstDerivativeRoots(xa, xb, xc, xd); | ||
for (i = 0; i < ts.length; i++) { | ||
t = ts[i]; | ||
for (i = 0; i < ts.length; i++) { | ||
if (t >= 0 && t <= 1) { | ||
x = computeCubicBaseValue(t, xa, xb, xc, xd); | ||
// y = computeCubicBaseValue(t, ya, yb, yc, yd); | ||
t = ts[i]; | ||
if (t >= 0 && t <= 1) { | ||
x = computeCubicBaseValue(t, xa, xb, xc, xd); | ||
// y = computeCubicBaseValue(t, ya, yb, yc, yd); | ||
if (x < minx) { minx = x; } | ||
if (x > maxx) { maxx = x; } | ||
} | ||
if (x < minx) { | ||
minx = x; | ||
} | ||
if (x > maxx) { | ||
maxx = x; | ||
} | ||
} | ||
} | ||
// Y | ||
if (ya < miny) { miny = ya; } | ||
if (ya > maxy) { maxy = ya; } | ||
if (yd < miny) { miny = yd; } | ||
if (yd > maxy) { maxy = yd; } | ||
// Y | ||
if (ya < miny) { | ||
miny = ya; | ||
} | ||
if (ya > maxy) { | ||
maxy = ya; | ||
} | ||
if (yd < miny) { | ||
miny = yd; | ||
} | ||
if (yd > maxy) { | ||
maxy = yd; | ||
} | ||
ts = computeCubicFirstDerivativeRoots(ya, yb, yc, yd); | ||
ts = computeCubicFirstDerivativeRoots(ya, yb, yc, yd); | ||
for (i = 0; i < ts.length; i++) { | ||
for (i = 0; i < ts.length; i++) { | ||
t = ts[i]; | ||
t = ts[i]; | ||
if (t >= 0 && t <= 1) { | ||
// x = computeCubicBaseValue(t, xa, xb, xc, xd); | ||
y = computeCubicBaseValue(t, ya, yb, yc, yd); | ||
if (t >= 0 && t <= 1) { | ||
// x = computeCubicBaseValue(t, xa, xb, xc, xd); | ||
y = computeCubicBaseValue(t, ya, yb, yc, yd); | ||
if (y < miny) { miny = y; } | ||
if (y > maxy) { maxy = y; } | ||
} | ||
if (y < miny) { | ||
miny = y; | ||
} | ||
if (y > maxy) { | ||
maxy = y; | ||
} | ||
} | ||
} | ||
return { | ||
minx: minx, | ||
miny: miny, | ||
maxx: maxx, | ||
maxy: maxy | ||
}; | ||
return { | ||
minx: minx, | ||
miny: miny, | ||
maxx: maxx, | ||
maxy: maxy, | ||
}; | ||
}; | ||
@@ -387,7 +404,7 @@ | ||
function computeCubicBaseValue(t, a, b, c, d) { | ||
var mt = 1 - t; | ||
var mt = 1 - t; | ||
return mt * mt * mt * a + 3 * mt * mt * t * b + 3 * mt * t * t * c + t * t * t * d; | ||
return ( | ||
mt * mt * mt * a + 3 * mt * mt * t * b + 3 * mt * t * t * c + t * t * t * d | ||
); | ||
} | ||
@@ -397,15 +414,13 @@ | ||
function computeCubicFirstDerivativeRoots(a, b, c, d) { | ||
var result = [-1, -1], | ||
tl = -a + 2 * b - c, | ||
tr = -Math.sqrt(-a * (c - d) + b * b - b * (c + d) + c * c), | ||
dn = -a + 3 * b - 3 * c + d; | ||
var result = [-1, -1], | ||
tl = -a + 2 * b - c, | ||
tr = -Math.sqrt(-a * (c - d) + b * b - b * (c + d) + c * c), | ||
dn = -a + 3 * b - 3 * c + d; | ||
if (dn !== 0) { | ||
result[0] = (tl + tr) / dn; | ||
result[1] = (tl - tr) / dn; | ||
} | ||
if (dn !== 0) { | ||
result[0] = (tl + tr) / dn; | ||
result[1] = (tl - tr) / dn; | ||
} | ||
return result; | ||
return result; | ||
} | ||
@@ -427,52 +442,73 @@ | ||
*/ | ||
exports.computeQuadraticBoundingBox = function(xa, ya, xb, yb, xc, yc) { | ||
exports.computeQuadraticBoundingBox = function (xa, ya, xb, yb, xc, yc) { | ||
var minx = Number.POSITIVE_INFINITY, | ||
miny = Number.POSITIVE_INFINITY, | ||
maxx = Number.NEGATIVE_INFINITY, | ||
maxy = Number.NEGATIVE_INFINITY, | ||
t, | ||
x, | ||
y; | ||
var minx = Number.POSITIVE_INFINITY, | ||
miny = Number.POSITIVE_INFINITY, | ||
maxx = Number.NEGATIVE_INFINITY, | ||
maxy = Number.NEGATIVE_INFINITY, | ||
t, | ||
x, | ||
y; | ||
// X | ||
if (xa < minx) { | ||
minx = xa; | ||
} | ||
if (xa > maxx) { | ||
maxx = xa; | ||
} | ||
if (xc < minx) { | ||
minx = xc; | ||
} | ||
if (xc > maxx) { | ||
maxx = xc; | ||
} | ||
// X | ||
if (xa < minx) { minx = xa; } | ||
if (xa > maxx) { maxx = xa; } | ||
if (xc < minx) { minx = xc; } | ||
if (xc > maxx) { maxx = xc; } | ||
t = computeQuadraticFirstDerivativeRoot(xa, xb, xc); | ||
t = computeQuadraticFirstDerivativeRoot(xa, xb, xc); | ||
if (t >= 0 && t <= 1) { | ||
x = computeQuadraticBaseValue(t, xa, xb, xc); | ||
// y = computeQuadraticBaseValue(t, ya, yb, yc); | ||
if (t >= 0 && t <= 1) { | ||
x = computeQuadraticBaseValue(t, xa, xb, xc); | ||
// y = computeQuadraticBaseValue(t, ya, yb, yc); | ||
if (x < minx) { minx = x; } | ||
if (x > maxx) { maxx = x; } | ||
if (x < minx) { | ||
minx = x; | ||
} | ||
if (x > maxx) { | ||
maxx = x; | ||
} | ||
} | ||
// Y | ||
if (ya < miny) { miny = ya; } | ||
if (ya > maxy) { maxy = ya; } | ||
if (yc < miny) { miny = yc; } | ||
if (yc > maxy) { maxy = yc; } | ||
// Y | ||
if (ya < miny) { | ||
miny = ya; | ||
} | ||
if (ya > maxy) { | ||
maxy = ya; | ||
} | ||
if (yc < miny) { | ||
miny = yc; | ||
} | ||
if (yc > maxy) { | ||
maxy = yc; | ||
} | ||
t = computeQuadraticFirstDerivativeRoot(ya, yb, yc); | ||
t = computeQuadraticFirstDerivativeRoot(ya, yb, yc); | ||
if (t >= 0 && t <=1 ) { | ||
// x = computeQuadraticBaseValue(t, xa, xb, xc); | ||
y = computeQuadraticBaseValue(t, ya, yb, yc); | ||
if (t >= 0 && t <= 1) { | ||
// x = computeQuadraticBaseValue(t, xa, xb, xc); | ||
y = computeQuadraticBaseValue(t, ya, yb, yc); | ||
if (y < miny) { miny = y; } | ||
if (y > maxy) { maxy = y ; } | ||
if (y < miny) { | ||
miny = y; | ||
} | ||
if (y > maxy) { | ||
maxy = y; | ||
} | ||
} | ||
return { | ||
minx: minx, | ||
miny: miny, | ||
maxx: maxx, | ||
maxy: maxy | ||
}; | ||
return { | ||
minx: minx, | ||
miny: miny, | ||
maxx: maxx, | ||
maxy: maxy, | ||
}; | ||
}; | ||
@@ -482,7 +518,5 @@ | ||
function computeQuadraticBaseValue(t, a, b, c) { | ||
var mt = 1 - t; | ||
var mt = 1 - t; | ||
return mt * mt * a + 2 * mt * t * b + t * t * c; | ||
return mt * mt * a + 2 * mt * t * b + t * t * c; | ||
} | ||
@@ -492,12 +526,10 @@ | ||
function computeQuadraticFirstDerivativeRoot(a, b, c) { | ||
var t = -1, | ||
denominator = a - 2 * b + c; | ||
var t = -1, | ||
denominator = a - 2 * b + c; | ||
if (denominator !== 0) { | ||
t = (a - b) / denominator; | ||
} | ||
if (denominator !== 0) { | ||
t = (a - b) / denominator; | ||
} | ||
return t; | ||
return t; | ||
} | ||
@@ -512,4 +544,3 @@ | ||
*/ | ||
exports.js2path = function(path, data, params) { | ||
exports.js2path = function (path, data, params) { | ||
path.pathJS = data; | ||
@@ -538,2 +569,3 @@ | ||
precision: params.floatPrecision, | ||
disableSpaceAfterFlags: params.noSpaceAfterFlags, | ||
}); | ||
@@ -543,5 +575,5 @@ }; | ||
function set(dest, source) { | ||
dest[0] = source[source.length - 2]; | ||
dest[1] = source[source.length - 1]; | ||
return dest; | ||
dest[0] = source[source.length - 2]; | ||
dest[1] = source[source.length - 1]; | ||
return dest; | ||
} | ||
@@ -558,221 +590,264 @@ | ||
*/ | ||
exports.intersects = function(path1, path2) { | ||
// Collect points of every subpath. | ||
var points1 = relative2absolute(path1).reduce(gatherPoints, []), | ||
points2 = relative2absolute(path2).reduce(gatherPoints, []); | ||
exports.intersects = function (path1, path2) { | ||
// Collect points of every subpath. | ||
var points1 = relative2absolute(path1).reduce(gatherPoints, []), | ||
points2 = relative2absolute(path2).reduce(gatherPoints, []); | ||
// Axis-aligned bounding box check. | ||
if (points1.maxX <= points2.minX || points2.maxX <= points1.minX || | ||
points1.maxY <= points2.minY || points2.maxY <= points1.minY || | ||
points1.every(function (set1) { | ||
return points2.every(function (set2) { | ||
return set1[set1.maxX][0] <= set2[set2.minX][0] || | ||
set2[set2.maxX][0] <= set1[set1.minX][0] || | ||
set1[set1.maxY][1] <= set2[set2.minY][1] || | ||
set2[set2.maxY][1] <= set1[set1.minY][1]; | ||
}); | ||
}) | ||
) return false; | ||
// Axis-aligned bounding box check. | ||
if ( | ||
points1.maxX <= points2.minX || | ||
points2.maxX <= points1.minX || | ||
points1.maxY <= points2.minY || | ||
points2.maxY <= points1.minY || | ||
points1.every(function (set1) { | ||
return points2.every(function (set2) { | ||
return ( | ||
set1[set1.maxX][0] <= set2[set2.minX][0] || | ||
set2[set2.maxX][0] <= set1[set1.minX][0] || | ||
set1[set1.maxY][1] <= set2[set2.minY][1] || | ||
set2[set2.maxY][1] <= set1[set1.minY][1] | ||
); | ||
}); | ||
}) | ||
) | ||
return false; | ||
// Get a convex hull from points of each subpath. Has the most complexity O(n·log n). | ||
var hullNest1 = points1.map(convexHull), | ||
hullNest2 = points2.map(convexHull); | ||
// Get a convex hull from points of each subpath. Has the most complexity O(n·log n). | ||
var hullNest1 = points1.map(convexHull), | ||
hullNest2 = points2.map(convexHull); | ||
// Check intersection of every subpath of the first path with every subpath of the second. | ||
return hullNest1.some(function(hull1) { | ||
if (hull1.length < 3) return false; | ||
// Check intersection of every subpath of the first path with every subpath of the second. | ||
return hullNest1.some(function (hull1) { | ||
if (hull1.length < 3) return false; | ||
return hullNest2.some(function(hull2) { | ||
if (hull2.length < 3) return false; | ||
return hullNest2.some(function (hull2) { | ||
if (hull2.length < 3) return false; | ||
var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex | ||
direction = minus(simplex[0]); // set the direction to point towards the origin | ||
var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex | ||
direction = minus(simplex[0]); // set the direction to point towards the origin | ||
var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
// eslint-disable-next-line no-constant-condition | ||
if (iterations-- == 0) { | ||
console.error('Error: infinite loop while processing mergePaths plugin.'); | ||
return true; // true is the safe value that means “do nothing with paths” | ||
} | ||
// add a new point | ||
simplex.push(getSupport(hull1, hull2, direction)); | ||
// see if the new point was on the correct side of the origin | ||
if (dot(direction, simplex[simplex.length - 1]) <= 0) return false; | ||
// process the simplex | ||
if (processSimplex(simplex, direction)) return true; | ||
} | ||
}); | ||
var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
// eslint-disable-next-line no-constant-condition | ||
if (iterations-- == 0) { | ||
console.error( | ||
'Error: infinite loop while processing mergePaths plugin.' | ||
); | ||
return true; // true is the safe value that means “do nothing with paths” | ||
} | ||
// add a new point | ||
simplex.push(getSupport(hull1, hull2, direction)); | ||
// see if the new point was on the correct side of the origin | ||
if (dot(direction, simplex[simplex.length - 1]) <= 0) return false; | ||
// process the simplex | ||
if (processSimplex(simplex, direction)) return true; | ||
} | ||
}); | ||
}); | ||
function getSupport(a, b, direction) { | ||
return sub(supportPoint(a, direction), supportPoint(b, minus(direction))); | ||
} | ||
function getSupport(a, b, direction) { | ||
return sub(supportPoint(a, direction), supportPoint(b, minus(direction))); | ||
} | ||
// Computes farthest polygon point in particular direction. | ||
// Thanks to knowledge of min/max x and y coordinates we can choose a quadrant to search in. | ||
// Since we're working on convex hull, the dot product is increasing until we find the farthest point. | ||
function supportPoint(polygon, direction) { | ||
var index = direction[1] >= 0 ? | ||
direction[0] < 0 ? polygon.maxY : polygon.maxX : | ||
direction[0] < 0 ? polygon.minX : polygon.minY, | ||
max = -Infinity, | ||
value; | ||
while ((value = dot(polygon[index], direction)) > max) { | ||
max = value; | ||
index = ++index % polygon.length; | ||
} | ||
return polygon[(index || polygon.length) - 1]; | ||
// Computes farthest polygon point in particular direction. | ||
// Thanks to knowledge of min/max x and y coordinates we can choose a quadrant to search in. | ||
// Since we're working on convex hull, the dot product is increasing until we find the farthest point. | ||
function supportPoint(polygon, direction) { | ||
var index = | ||
direction[1] >= 0 | ||
? direction[0] < 0 | ||
? polygon.maxY | ||
: polygon.maxX | ||
: direction[0] < 0 | ||
? polygon.minX | ||
: polygon.minY, | ||
max = -Infinity, | ||
value; | ||
while ((value = dot(polygon[index], direction)) > max) { | ||
max = value; | ||
index = ++index % polygon.length; | ||
} | ||
return polygon[(index || polygon.length) - 1]; | ||
} | ||
}; | ||
function processSimplex(simplex, direction) { | ||
// we only need to handle to 1-simplex and 2-simplex | ||
if (simplex.length == 2) { | ||
// 1-simplex | ||
let a = simplex[1], | ||
b = simplex[0], | ||
AO = minus(simplex[1]), | ||
AB = sub(b, a); | ||
// AO is in the same direction as AB | ||
if (dot(AO, AB) > 0) { | ||
// get the vector perpendicular to AB facing O | ||
set(direction, orth(AB, a)); | ||
} else { | ||
set(direction, AO); | ||
// only A remains in the simplex | ||
simplex.shift(); | ||
} | ||
} else { | ||
// 2-simplex | ||
let a = simplex[2], // [a, b, c] = simplex | ||
b = simplex[1], | ||
c = simplex[0], | ||
AB = sub(b, a), | ||
AC = sub(c, a), | ||
AO = minus(a), | ||
ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C | ||
ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B | ||
// we only need to handle to 1-simplex and 2-simplex | ||
if (simplex.length == 2) { // 1-simplex | ||
let a = simplex[1], | ||
b = simplex[0], | ||
AO = minus(simplex[1]), | ||
AB = sub(b, a); | ||
// AO is in the same direction as AB | ||
if (dot(AO, AB) > 0) { | ||
// get the vector perpendicular to AB facing O | ||
set(direction, orth(AB, a)); | ||
} else { | ||
set(direction, AO); | ||
// only A remains in the simplex | ||
simplex.shift(); | ||
} | ||
} else { // 2-simplex | ||
let a = simplex[2], // [a, b, c] = simplex | ||
b = simplex[1], | ||
c = simplex[0], | ||
AB = sub(b, a), | ||
AC = sub(c, a), | ||
AO = minus(a), | ||
ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C | ||
ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B | ||
if (dot(ACB, AO) > 0) { | ||
if (dot(AB, AO) > 0) { // region 4 | ||
set(direction, ACB); | ||
simplex.shift(); // simplex = [b, a] | ||
} else { // region 5 | ||
set(direction, AO); | ||
simplex.splice(0, 2); // simplex = [a] | ||
} | ||
} else if (dot(ABC, AO) > 0) { | ||
if (dot(AC, AO) > 0) { // region 6 | ||
set(direction, ABC); | ||
simplex.splice(1, 1); // simplex = [c, a] | ||
} else { // region 5 (again) | ||
set(direction, AO); | ||
simplex.splice(0, 2); // simplex = [a] | ||
} | ||
} else // region 7 | ||
return true; | ||
} | ||
return false; | ||
if (dot(ACB, AO) > 0) { | ||
if (dot(AB, AO) > 0) { | ||
// region 4 | ||
set(direction, ACB); | ||
simplex.shift(); // simplex = [b, a] | ||
} else { | ||
// region 5 | ||
set(direction, AO); | ||
simplex.splice(0, 2); // simplex = [a] | ||
} | ||
} else if (dot(ABC, AO) > 0) { | ||
if (dot(AC, AO) > 0) { | ||
// region 6 | ||
set(direction, ABC); | ||
simplex.splice(1, 1); // simplex = [c, a] | ||
} else { | ||
// region 5 (again) | ||
set(direction, AO); | ||
simplex.splice(0, 2); // simplex = [a] | ||
} | ||
} // region 7 | ||
else return true; | ||
} | ||
return false; | ||
} | ||
function minus(v) { | ||
return [-v[0], -v[1]]; | ||
return [-v[0], -v[1]]; | ||
} | ||
function sub(v1, v2) { | ||
return [v1[0] - v2[0], v1[1] - v2[1]]; | ||
return [v1[0] - v2[0], v1[1] - v2[1]]; | ||
} | ||
function dot(v1, v2) { | ||
return v1[0] * v2[0] + v1[1] * v2[1]; | ||
return v1[0] * v2[0] + v1[1] * v2[1]; | ||
} | ||
function orth(v, from) { | ||
var o = [-v[1], v[0]]; | ||
return dot(o, minus(from)) < 0 ? minus(o) : o; | ||
var o = [-v[1], v[0]]; | ||
return dot(o, minus(from)) < 0 ? minus(o) : o; | ||
} | ||
function gatherPoints(points, item, index, path) { | ||
var subPath = points.length && points[points.length - 1], | ||
prev = index && path[index - 1], | ||
basePoint = subPath.length && subPath[subPath.length - 1], | ||
data = item.data, | ||
ctrlPoint = basePoint; | ||
var subPath = points.length && points[points.length - 1], | ||
prev = index && path[index - 1], | ||
basePoint = subPath.length && subPath[subPath.length - 1], | ||
data = item.data, | ||
ctrlPoint = basePoint; | ||
switch (item.instruction) { | ||
case 'M': | ||
points.push((subPath = [])); | ||
break; | ||
case 'H': | ||
addPoint(subPath, [data[0], basePoint[1]]); | ||
break; | ||
case 'V': | ||
addPoint(subPath, [basePoint[0], data[0]]); | ||
break; | ||
case 'Q': | ||
addPoint(subPath, data.slice(0, 2)); | ||
prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; // Save control point for shorthand | ||
break; | ||
case 'T': | ||
if (prev.instruction == 'Q' || prev.instruction == 'T') { | ||
ctrlPoint = [ | ||
basePoint[0] + prevCtrlPoint[0], | ||
basePoint[1] + prevCtrlPoint[1], | ||
]; | ||
addPoint(subPath, ctrlPoint); | ||
prevCtrlPoint = [data[0] - ctrlPoint[0], data[1] - ctrlPoint[1]]; | ||
} | ||
break; | ||
case 'C': | ||
// Approximate quibic Bezier curve with middle points between control points | ||
addPoint(subPath, [ | ||
0.5 * (basePoint[0] + data[0]), | ||
0.5 * (basePoint[1] + data[1]), | ||
]); | ||
addPoint(subPath, [0.5 * (data[0] + data[2]), 0.5 * (data[1] + data[3])]); | ||
addPoint(subPath, [0.5 * (data[2] + data[4]), 0.5 * (data[3] + data[5])]); | ||
prevCtrlPoint = [data[4] - data[2], data[5] - data[3]]; // Save control point for shorthand | ||
break; | ||
case 'S': | ||
if (prev.instruction == 'C' || prev.instruction == 'S') { | ||
addPoint(subPath, [ | ||
basePoint[0] + 0.5 * prevCtrlPoint[0], | ||
basePoint[1] + 0.5 * prevCtrlPoint[1], | ||
]); | ||
ctrlPoint = [ | ||
basePoint[0] + prevCtrlPoint[0], | ||
basePoint[1] + prevCtrlPoint[1], | ||
]; | ||
} | ||
addPoint(subPath, [ | ||
0.5 * (ctrlPoint[0] + data[0]), | ||
0.5 * (ctrlPoint[1] + data[1]), | ||
]); | ||
addPoint(subPath, [0.5 * (data[0] + data[2]), 0.5 * (data[1] + data[3])]); | ||
prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; | ||
break; | ||
case 'A': | ||
// Convert the arc to bezier curves and use the same approximation | ||
var curves = a2c.apply(0, basePoint.concat(data)); | ||
for (var cData; (cData = curves.splice(0, 6).map(toAbsolute)).length; ) { | ||
addPoint(subPath, [ | ||
0.5 * (basePoint[0] + cData[0]), | ||
0.5 * (basePoint[1] + cData[1]), | ||
]); | ||
addPoint(subPath, [ | ||
0.5 * (cData[0] + cData[2]), | ||
0.5 * (cData[1] + cData[3]), | ||
]); | ||
addPoint(subPath, [ | ||
0.5 * (cData[2] + cData[4]), | ||
0.5 * (cData[3] + cData[5]), | ||
]); | ||
if (curves.length) addPoint(subPath, (basePoint = cData.slice(-2))); | ||
} | ||
break; | ||
} | ||
// Save final command coordinates | ||
if (data && data.length >= 2) addPoint(subPath, data.slice(-2)); | ||
return points; | ||
switch (item.instruction) { | ||
case 'M': | ||
points.push(subPath = []); | ||
break; | ||
case 'H': | ||
addPoint(subPath, [data[0], basePoint[1]]); | ||
break; | ||
case 'V': | ||
addPoint(subPath, [basePoint[0], data[0]]); | ||
break; | ||
case 'Q': | ||
addPoint(subPath, data.slice(0, 2)); | ||
prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; // Save control point for shorthand | ||
break; | ||
case 'T': | ||
if (prev.instruction == 'Q' || prev.instruction == 'T') { | ||
ctrlPoint = [basePoint[0] + prevCtrlPoint[0], basePoint[1] + prevCtrlPoint[1]]; | ||
addPoint(subPath, ctrlPoint); | ||
prevCtrlPoint = [data[0] - ctrlPoint[0], data[1] - ctrlPoint[1]]; | ||
} | ||
break; | ||
case 'C': | ||
// Approximate quibic Bezier curve with middle points between control points | ||
addPoint(subPath, [.5 * (basePoint[0] + data[0]), .5 * (basePoint[1] + data[1])]); | ||
addPoint(subPath, [.5 * (data[0] + data[2]), .5 * (data[1] + data[3])]); | ||
addPoint(subPath, [.5 * (data[2] + data[4]), .5 * (data[3] + data[5])]); | ||
prevCtrlPoint = [data[4] - data[2], data[5] - data[3]]; // Save control point for shorthand | ||
break; | ||
case 'S': | ||
if (prev.instruction == 'C' || prev.instruction == 'S') { | ||
addPoint(subPath, [basePoint[0] + .5 * prevCtrlPoint[0], basePoint[1] + .5 * prevCtrlPoint[1]]); | ||
ctrlPoint = [basePoint[0] + prevCtrlPoint[0], basePoint[1] + prevCtrlPoint[1]]; | ||
} | ||
addPoint(subPath, [.5 * (ctrlPoint[0] + data[0]), .5 * (ctrlPoint[1]+ data[1])]); | ||
addPoint(subPath, [.5 * (data[0] + data[2]), .5 * (data[1] + data[3])]); | ||
prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; | ||
break; | ||
case 'A': | ||
// Convert the arc to bezier curves and use the same approximation | ||
var curves = a2c.apply(0, basePoint.concat(data)); | ||
for (var cData; (cData = curves.splice(0,6).map(toAbsolute)).length;) { | ||
addPoint(subPath, [.5 * (basePoint[0] + cData[0]), .5 * (basePoint[1] + cData[1])]); | ||
addPoint(subPath, [.5 * (cData[0] + cData[2]), .5 * (cData[1] + cData[3])]); | ||
addPoint(subPath, [.5 * (cData[2] + cData[4]), .5 * (cData[3] + cData[5])]); | ||
if (curves.length) addPoint(subPath, basePoint = cData.slice(-2)); | ||
} | ||
break; | ||
} | ||
// Save final command coordinates | ||
if (data && data.length >= 2) addPoint(subPath, data.slice(-2)); | ||
return points; | ||
function toAbsolute(n, i) { | ||
return n + basePoint[i % 2]; | ||
} | ||
function toAbsolute(n, i) { return n + basePoint[i % 2] } | ||
// Writes data about the extreme points on each axle | ||
function addPoint(path, point) { | ||
if (!path.length || point[1] > path[path.maxY][1]) { | ||
path.maxY = path.length; | ||
points.maxY = points.length ? Math.max(point[1], points.maxY) : point[1]; | ||
} | ||
if (!path.length || point[0] > path[path.maxX][0]) { | ||
path.maxX = path.length; | ||
points.maxX = points.length ? Math.max(point[0], points.maxX) : point[0]; | ||
} | ||
if (!path.length || point[1] < path[path.minY][1]) { | ||
path.minY = path.length; | ||
points.minY = points.length ? Math.min(point[1], points.minY) : point[1]; | ||
} | ||
if (!path.length || point[0] < path[path.minX][0]) { | ||
path.minX = path.length; | ||
points.minX = points.length ? Math.min(point[0], points.minX) : point[0]; | ||
} | ||
path.push(point); | ||
// Writes data about the extreme points on each axle | ||
function addPoint(path, point) { | ||
if (!path.length || point[1] > path[path.maxY][1]) { | ||
path.maxY = path.length; | ||
points.maxY = points.length ? Math.max(point[1], points.maxY) : point[1]; | ||
} | ||
if (!path.length || point[0] > path[path.maxX][0]) { | ||
path.maxX = path.length; | ||
points.maxX = points.length ? Math.max(point[0], points.maxX) : point[0]; | ||
} | ||
if (!path.length || point[1] < path[path.minY][1]) { | ||
path.minY = path.length; | ||
points.minY = points.length ? Math.min(point[1], points.minY) : point[1]; | ||
} | ||
if (!path.length || point[0] < path[path.minX][0]) { | ||
path.minX = path.length; | ||
points.minX = points.length ? Math.min(point[0], points.minX) : point[0]; | ||
} | ||
path.push(point); | ||
} | ||
} | ||
@@ -787,51 +862,56 @@ | ||
function convexHull(points) { | ||
points.sort(function (a, b) { | ||
return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]; | ||
}); | ||
points.sort(function(a, b) { | ||
return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]; | ||
}); | ||
var lower = [], | ||
minY = 0, | ||
bottom = 0; | ||
for (let i = 0; i < points.length; i++) { | ||
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0) { | ||
lower.pop(); | ||
} | ||
if (points[i][1] < points[minY][1]) { | ||
minY = i; | ||
bottom = lower.length; | ||
} | ||
lower.push(points[i]); | ||
var lower = [], | ||
minY = 0, | ||
bottom = 0; | ||
for (let i = 0; i < points.length; i++) { | ||
while ( | ||
lower.length >= 2 && | ||
cross(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0 | ||
) { | ||
lower.pop(); | ||
} | ||
if (points[i][1] < points[minY][1]) { | ||
minY = i; | ||
bottom = lower.length; | ||
} | ||
lower.push(points[i]); | ||
} | ||
var upper = [], | ||
maxY = points.length - 1, | ||
top = 0; | ||
for (let i = points.length; i--;) { | ||
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], points[i]) <= 0) { | ||
upper.pop(); | ||
} | ||
if (points[i][1] > points[maxY][1]) { | ||
maxY = i; | ||
top = upper.length; | ||
} | ||
upper.push(points[i]); | ||
var upper = [], | ||
maxY = points.length - 1, | ||
top = 0; | ||
for (let i = points.length; i--; ) { | ||
while ( | ||
upper.length >= 2 && | ||
cross(upper[upper.length - 2], upper[upper.length - 1], points[i]) <= 0 | ||
) { | ||
upper.pop(); | ||
} | ||
if (points[i][1] > points[maxY][1]) { | ||
maxY = i; | ||
top = upper.length; | ||
} | ||
upper.push(points[i]); | ||
} | ||
// last points are equal to starting points of the other part | ||
upper.pop(); | ||
lower.pop(); | ||
// last points are equal to starting points of the other part | ||
upper.pop(); | ||
lower.pop(); | ||
var hull = lower.concat(upper); | ||
var hull = lower.concat(upper); | ||
hull.minX = 0; // by sorting | ||
hull.maxX = lower.length; | ||
hull.minY = bottom; | ||
hull.maxY = (lower.length + top) % hull.length; | ||
hull.minX = 0; // by sorting | ||
hull.maxX = lower.length; | ||
hull.minY = bottom; | ||
hull.maxY = (lower.length + top) % hull.length; | ||
return hull; | ||
return hull; | ||
} | ||
function cross(o, a, b) { | ||
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]); | ||
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]); | ||
} | ||
@@ -843,81 +923,113 @@ | ||
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { | ||
// for more information of where this Math came from visit: | ||
// https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes | ||
var _120 = Math.PI * 120 / 180, | ||
rad = Math.PI / 180 * (+angle || 0), | ||
res = [], | ||
rotateX = function(x, y, rad) { return x * Math.cos(rad) - y * Math.sin(rad) }, | ||
rotateY = function(x, y, rad) { return x * Math.sin(rad) + y * Math.cos(rad) }; | ||
if (!recursive) { | ||
x1 = rotateX(x1, y1, -rad); | ||
y1 = rotateY(x1, y1, -rad); | ||
x2 = rotateX(x2, y2, -rad); | ||
y2 = rotateY(x2, y2, -rad); | ||
var x = (x1 - x2) / 2, | ||
y = (y1 - y2) / 2; | ||
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); | ||
if (h > 1) { | ||
h = Math.sqrt(h); | ||
rx = h * rx; | ||
ry = h * ry; | ||
} | ||
var rx2 = rx * rx, | ||
ry2 = ry * ry, | ||
k = (large_arc_flag == sweep_flag ? -1 : 1) * | ||
Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), | ||
cx = k * rx * y / ry + (x1 + x2) / 2, | ||
cy = k * -ry * x / rx + (y1 + y2) / 2, | ||
f1 = Math.asin(((y1 - cy) / ry).toFixed(9)), | ||
f2 = Math.asin(((y2 - cy) / ry).toFixed(9)); | ||
function a2c( | ||
x1, | ||
y1, | ||
rx, | ||
ry, | ||
angle, | ||
large_arc_flag, | ||
sweep_flag, | ||
x2, | ||
y2, | ||
recursive | ||
) { | ||
// for more information of where this Math came from visit: | ||
// https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes | ||
var _120 = (Math.PI * 120) / 180, | ||
rad = (Math.PI / 180) * (+angle || 0), | ||
res = [], | ||
rotateX = function (x, y, rad) { | ||
return x * Math.cos(rad) - y * Math.sin(rad); | ||
}, | ||
rotateY = function (x, y, rad) { | ||
return x * Math.sin(rad) + y * Math.cos(rad); | ||
}; | ||
if (!recursive) { | ||
x1 = rotateX(x1, y1, -rad); | ||
y1 = rotateY(x1, y1, -rad); | ||
x2 = rotateX(x2, y2, -rad); | ||
y2 = rotateY(x2, y2, -rad); | ||
var x = (x1 - x2) / 2, | ||
y = (y1 - y2) / 2; | ||
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); | ||
if (h > 1) { | ||
h = Math.sqrt(h); | ||
rx = h * rx; | ||
ry = h * ry; | ||
} | ||
var rx2 = rx * rx, | ||
ry2 = ry * ry, | ||
k = | ||
(large_arc_flag == sweep_flag ? -1 : 1) * | ||
Math.sqrt( | ||
Math.abs( | ||
(rx2 * ry2 - rx2 * y * y - ry2 * x * x) / | ||
(rx2 * y * y + ry2 * x * x) | ||
) | ||
), | ||
cx = (k * rx * y) / ry + (x1 + x2) / 2, | ||
cy = (k * -ry * x) / rx + (y1 + y2) / 2, | ||
f1 = Math.asin(((y1 - cy) / ry).toFixed(9)), | ||
f2 = Math.asin(((y2 - cy) / ry).toFixed(9)); | ||
f1 = x1 < cx ? Math.PI - f1 : f1; | ||
f2 = x2 < cx ? Math.PI - f2 : f2; | ||
f1 < 0 && (f1 = Math.PI * 2 + f1); | ||
f2 < 0 && (f2 = Math.PI * 2 + f2); | ||
if (sweep_flag && f1 > f2) { | ||
f1 = f1 - Math.PI * 2; | ||
} | ||
if (!sweep_flag && f2 > f1) { | ||
f2 = f2 - Math.PI * 2; | ||
} | ||
} else { | ||
f1 = recursive[0]; | ||
f2 = recursive[1]; | ||
cx = recursive[2]; | ||
cy = recursive[3]; | ||
f1 = x1 < cx ? Math.PI - f1 : f1; | ||
f2 = x2 < cx ? Math.PI - f2 : f2; | ||
f1 < 0 && (f1 = Math.PI * 2 + f1); | ||
f2 < 0 && (f2 = Math.PI * 2 + f2); | ||
if (sweep_flag && f1 > f2) { | ||
f1 = f1 - Math.PI * 2; | ||
} | ||
var df = f2 - f1; | ||
if (Math.abs(df) > _120) { | ||
var f2old = f2, | ||
x2old = x2, | ||
y2old = y2; | ||
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); | ||
x2 = cx + rx * Math.cos(f2); | ||
y2 = cy + ry * Math.sin(f2); | ||
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); | ||
if (!sweep_flag && f2 > f1) { | ||
f2 = f2 - Math.PI * 2; | ||
} | ||
df = f2 - f1; | ||
var c1 = Math.cos(f1), | ||
s1 = Math.sin(f1), | ||
c2 = Math.cos(f2), | ||
s2 = Math.sin(f2), | ||
t = Math.tan(df / 4), | ||
hx = 4 / 3 * rx * t, | ||
hy = 4 / 3 * ry * t, | ||
m = [ | ||
- hx * s1, hy * c1, | ||
x2 + hx * s2 - x1, y2 - hy * c2 - y1, | ||
x2 - x1, y2 - y1 | ||
]; | ||
if (recursive) { | ||
return m.concat(res); | ||
} else { | ||
res = m.concat(res); | ||
var newres = []; | ||
for (var i = 0, n = res.length; i < n; i++) { | ||
newres[i] = i % 2 ? rotateY(res[i - 1], res[i], rad) : rotateX(res[i], res[i + 1], rad); | ||
} | ||
return newres; | ||
} else { | ||
f1 = recursive[0]; | ||
f2 = recursive[1]; | ||
cx = recursive[2]; | ||
cy = recursive[3]; | ||
} | ||
var df = f2 - f1; | ||
if (Math.abs(df) > _120) { | ||
var f2old = f2, | ||
x2old = x2, | ||
y2old = y2; | ||
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); | ||
x2 = cx + rx * Math.cos(f2); | ||
y2 = cy + ry * Math.sin(f2); | ||
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [ | ||
f2, | ||
f2old, | ||
cx, | ||
cy, | ||
]); | ||
} | ||
df = f2 - f1; | ||
var c1 = Math.cos(f1), | ||
s1 = Math.sin(f1), | ||
c2 = Math.cos(f2), | ||
s2 = Math.sin(f2), | ||
t = Math.tan(df / 4), | ||
hx = (4 / 3) * rx * t, | ||
hy = (4 / 3) * ry * t, | ||
m = [ | ||
-hx * s1, | ||
hy * c1, | ||
x2 + hx * s2 - x1, | ||
y2 - hy * c2 - y1, | ||
x2 - x1, | ||
y2 - y1, | ||
]; | ||
if (recursive) { | ||
return m.concat(res); | ||
} else { | ||
res = m.concat(res); | ||
var newres = []; | ||
for (var i = 0, n = res.length; i < n; i++) { | ||
newres[i] = | ||
i % 2 | ||
? rotateY(res[i - 1], res[i], rad) | ||
: rotateX(res[i], res[i + 1], rad); | ||
} | ||
return newres; | ||
} | ||
} |
'use strict'; | ||
var regTransformTypes = /matrix|translate|scale|rotate|skewX|skewY/, | ||
regTransformSplit = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/, | ||
regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; | ||
regTransformSplit = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/, | ||
regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; | ||
@@ -14,35 +14,32 @@ /** | ||
*/ | ||
exports.transform2js = function(transformString) { | ||
exports.transform2js = function (transformString) { | ||
// JS representation of the transform data | ||
var transforms = [], | ||
// current transform context | ||
current; | ||
// JS representation of the transform data | ||
var transforms = [], | ||
// current transform context | ||
current; | ||
// split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', ''] | ||
transformString.split(regTransformSplit).forEach(function (item) { | ||
var num; | ||
// split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', ''] | ||
transformString.split(regTransformSplit).forEach(function(item) { | ||
var num; | ||
if (item) { | ||
// if item is a translate function | ||
if (regTransformTypes.test(item)) { | ||
// then collect it and change current context | ||
transforms.push(current = { name: item }); | ||
// else if item is data | ||
} else { | ||
// then split it into [10, 50] and collect as context.data | ||
// eslint-disable-next-line no-cond-assign | ||
while (num = regNumericValues.exec(item)) { | ||
num = Number(num); | ||
if (current.data) | ||
current.data.push(num); | ||
else | ||
current.data = [num]; | ||
} | ||
} | ||
if (item) { | ||
// if item is a translate function | ||
if (regTransformTypes.test(item)) { | ||
// then collect it and change current context | ||
transforms.push((current = { name: item })); | ||
// else if item is data | ||
} else { | ||
// then split it into [10, 50] and collect as context.data | ||
// eslint-disable-next-line no-cond-assign | ||
while ((num = regNumericValues.exec(item))) { | ||
num = Number(num); | ||
if (current.data) current.data.push(num); | ||
else current.data = [num]; | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
// return empty array if broken transform (no data) | ||
return current && current.data ? transforms : []; | ||
// return empty array if broken transform (no data) | ||
return current && current.data ? transforms : []; | ||
}; | ||
@@ -56,20 +53,19 @@ | ||
*/ | ||
exports.transformsMultiply = function(transforms) { | ||
exports.transformsMultiply = function (transforms) { | ||
// convert transforms objects to the matrices | ||
transforms = transforms.map(function (transform) { | ||
if (transform.name === 'matrix') { | ||
return transform.data; | ||
} | ||
return transformToMatrix(transform); | ||
}); | ||
// convert transforms objects to the matrices | ||
transforms = transforms.map(function(transform) { | ||
if (transform.name === 'matrix') { | ||
return transform.data; | ||
} | ||
return transformToMatrix(transform); | ||
}); | ||
// multiply all matrices into one | ||
transforms = { | ||
name: 'matrix', | ||
data: | ||
transforms.length > 0 ? transforms.reduce(multiplyTransformMatrices) : [], | ||
}; | ||
// multiply all matrices into one | ||
transforms = { | ||
name: 'matrix', | ||
data: transforms.length > 0 ? transforms.reduce(multiplyTransformMatrices) : [] | ||
}; | ||
return transforms; | ||
return transforms; | ||
}; | ||
@@ -82,38 +78,36 @@ | ||
*/ | ||
var mth = exports.mth = { | ||
var mth = (exports.mth = { | ||
rad: function (deg) { | ||
return (deg * Math.PI) / 180; | ||
}, | ||
rad: function(deg) { | ||
return deg * Math.PI / 180; | ||
}, | ||
deg: function (rad) { | ||
return (rad * 180) / Math.PI; | ||
}, | ||
deg: function(rad) { | ||
return rad * 180 / Math.PI; | ||
}, | ||
cos: function (deg) { | ||
return Math.cos(this.rad(deg)); | ||
}, | ||
cos: function(deg) { | ||
return Math.cos(this.rad(deg)); | ||
}, | ||
acos: function (val, floatPrecision) { | ||
return +this.deg(Math.acos(val)).toFixed(floatPrecision); | ||
}, | ||
acos: function(val, floatPrecision) { | ||
return +(this.deg(Math.acos(val)).toFixed(floatPrecision)); | ||
}, | ||
sin: function (deg) { | ||
return Math.sin(this.rad(deg)); | ||
}, | ||
sin: function(deg) { | ||
return Math.sin(this.rad(deg)); | ||
}, | ||
asin: function (val, floatPrecision) { | ||
return +this.deg(Math.asin(val)).toFixed(floatPrecision); | ||
}, | ||
asin: function(val, floatPrecision) { | ||
return +(this.deg(Math.asin(val)).toFixed(floatPrecision)); | ||
}, | ||
tan: function (deg) { | ||
return Math.tan(this.rad(deg)); | ||
}, | ||
tan: function(deg) { | ||
return Math.tan(this.rad(deg)); | ||
}, | ||
atan: function (val, floatPrecision) { | ||
return +this.deg(Math.atan(val)).toFixed(floatPrecision); | ||
}, | ||
}); | ||
atan: function(val, floatPrecision) { | ||
return +(this.deg(Math.atan(val)).toFixed(floatPrecision)); | ||
} | ||
}; | ||
/** | ||
@@ -126,68 +120,85 @@ * Decompose matrix into simple transforms. See | ||
*/ | ||
exports.matrixToTransform = function(transform, params) { | ||
var floatPrecision = params.floatPrecision, | ||
data = transform.data, | ||
transforms = [], | ||
sx = +Math.hypot(data[0], data[1]).toFixed(params.transformPrecision), | ||
sy = +((data[0] * data[3] - data[1] * data[2]) / sx).toFixed(params.transformPrecision), | ||
colsSum = data[0] * data[2] + data[1] * data[3], | ||
rowsSum = data[0] * data[1] + data[2] * data[3], | ||
scaleBefore = rowsSum != 0 || sx == sy; | ||
exports.matrixToTransform = function (transform, params) { | ||
var floatPrecision = params.floatPrecision, | ||
data = transform.data, | ||
transforms = [], | ||
sx = +Math.hypot(data[0], data[1]).toFixed(params.transformPrecision), | ||
sy = +((data[0] * data[3] - data[1] * data[2]) / sx).toFixed( | ||
params.transformPrecision | ||
), | ||
colsSum = data[0] * data[2] + data[1] * data[3], | ||
rowsSum = data[0] * data[1] + data[2] * data[3], | ||
scaleBefore = rowsSum != 0 || sx == sy; | ||
// [..., ..., ..., ..., tx, ty] → translate(tx, ty) | ||
if (data[4] || data[5]) { | ||
transforms.push({ name: 'translate', data: data.slice(4, data[5] ? 6 : 5) }); | ||
} | ||
// [..., ..., ..., ..., tx, ty] → translate(tx, ty) | ||
if (data[4] || data[5]) { | ||
transforms.push({ | ||
name: 'translate', | ||
data: data.slice(4, data[5] ? 6 : 5), | ||
}); | ||
} | ||
// [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy) | ||
if (!data[1] && data[2]) { | ||
transforms.push({ name: 'skewX', data: [mth.atan(data[2] / sy, floatPrecision)] }); | ||
// [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy) | ||
if (!data[1] && data[2]) { | ||
transforms.push({ | ||
name: 'skewX', | ||
data: [mth.atan(data[2] / sy, floatPrecision)], | ||
}); | ||
// [sx, sx·tan(a), 0, sy, 0, 0] → skewY(a)·scale(sx, sy) | ||
} else if (data[1] && !data[2]) { | ||
transforms.push({ name: 'skewY', data: [mth.atan(data[1] / data[0], floatPrecision)] }); | ||
sx = data[0]; | ||
sy = data[3]; | ||
} else if (data[1] && !data[2]) { | ||
transforms.push({ | ||
name: 'skewY', | ||
data: [mth.atan(data[1] / data[0], floatPrecision)], | ||
}); | ||
sx = data[0]; | ||
sy = data[3]; | ||
// [sx·cos(a), sx·sin(a), sy·-sin(a), sy·cos(a), x, y] → rotate(a[, cx, cy])·(scale or skewX) or | ||
// [sx·cos(a), sy·sin(a), sx·-sin(a), sy·cos(a), x, y] → scale(sx, sy)·rotate(a[, cx, cy]) (if !scaleBefore) | ||
} else if (!colsSum || (sx == 1 && sy == 1) || !scaleBefore) { | ||
if (!scaleBefore) { | ||
sx = (data[0] < 0 ? -1 : 1) * Math.hypot(data[0], data[2]); | ||
sy = (data[3] < 0 ? -1 : 1) * Math.hypot(data[1], data[3]); | ||
transforms.push({ name: 'scale', data: [sx, sy] }); | ||
} | ||
var angle = Math.min(Math.max(-1, data[0] / sx), 1), | ||
rotate = [mth.acos(angle, floatPrecision) * ((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1)]; | ||
} else if (!colsSum || (sx == 1 && sy == 1) || !scaleBefore) { | ||
if (!scaleBefore) { | ||
sx = (data[0] < 0 ? -1 : 1) * Math.hypot(data[0], data[2]); | ||
sy = (data[3] < 0 ? -1 : 1) * Math.hypot(data[1], data[3]); | ||
transforms.push({ name: 'scale', data: [sx, sy] }); | ||
} | ||
var angle = Math.min(Math.max(-1, data[0] / sx), 1), | ||
rotate = [ | ||
mth.acos(angle, floatPrecision) * | ||
((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1), | ||
]; | ||
if (rotate[0]) transforms.push({ name: 'rotate', data: rotate }); | ||
if (rotate[0]) transforms.push({ name: 'rotate', data: rotate }); | ||
if (rowsSum && colsSum) transforms.push({ | ||
name: 'skewX', | ||
data: [mth.atan(colsSum / (sx * sx), floatPrecision)] | ||
}); | ||
if (rowsSum && colsSum) | ||
transforms.push({ | ||
name: 'skewX', | ||
data: [mth.atan(colsSum / (sx * sx), floatPrecision)], | ||
}); | ||
// rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point) | ||
if (rotate[0] && (data[4] || data[5])) { | ||
transforms.shift(); | ||
var cos = data[0] / sx, | ||
sin = data[1] / (scaleBefore ? sx : sy), | ||
x = data[4] * (scaleBefore || sy), | ||
y = data[5] * (scaleBefore || sx), | ||
denom = (Math.pow(1 - cos, 2) + Math.pow(sin, 2)) * (scaleBefore || sx * sy); | ||
rotate.push(((1 - cos) * x - sin * y) / denom); | ||
rotate.push(((1 - cos) * y + sin * x) / denom); | ||
} | ||
// rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point) | ||
if (rotate[0] && (data[4] || data[5])) { | ||
transforms.shift(); | ||
var cos = data[0] / sx, | ||
sin = data[1] / (scaleBefore ? sx : sy), | ||
x = data[4] * (scaleBefore || sy), | ||
y = data[5] * (scaleBefore || sx), | ||
denom = | ||
(Math.pow(1 - cos, 2) + Math.pow(sin, 2)) * (scaleBefore || sx * sy); | ||
rotate.push(((1 - cos) * x - sin * y) / denom); | ||
rotate.push(((1 - cos) * y + sin * x) / denom); | ||
} | ||
// Too many transformations, return original matrix if it isn't just a scale/translate | ||
} else if (data[1] || data[2]) { | ||
return transform; | ||
} | ||
} else if (data[1] || data[2]) { | ||
return transform; | ||
} | ||
if (scaleBefore && (sx != 1 || sy != 1) || !transforms.length) transforms.push({ | ||
name: 'scale', | ||
data: sx == sy ? [sx] : [sx, sy] | ||
if ((scaleBefore && (sx != 1 || sy != 1)) || !transforms.length) | ||
transforms.push({ | ||
name: 'scale', | ||
data: sx == sy ? [sx] : [sx, sy], | ||
}); | ||
return transforms; | ||
return transforms; | ||
}; | ||
@@ -202,37 +213,49 @@ | ||
function transformToMatrix(transform) { | ||
if (transform.name === 'matrix') return transform.data; | ||
if (transform.name === 'matrix') return transform.data; | ||
var matrix; | ||
var matrix; | ||
switch (transform.name) { | ||
case 'translate': | ||
// [1, 0, 0, 1, tx, ty] | ||
matrix = [1, 0, 0, 1, transform.data[0], transform.data[1] || 0]; | ||
break; | ||
case 'scale': | ||
// [sx, 0, 0, sy, 0, 0] | ||
matrix = [ | ||
transform.data[0], | ||
0, | ||
0, | ||
transform.data[1] || transform.data[0], | ||
0, | ||
0, | ||
]; | ||
break; | ||
case 'rotate': | ||
// [cos(a), sin(a), -sin(a), cos(a), x, y] | ||
var cos = mth.cos(transform.data[0]), | ||
sin = mth.sin(transform.data[0]), | ||
cx = transform.data[1] || 0, | ||
cy = transform.data[2] || 0; | ||
switch (transform.name) { | ||
case 'translate': | ||
// [1, 0, 0, 1, tx, ty] | ||
matrix = [1, 0, 0, 1, transform.data[0], transform.data[1] || 0]; | ||
break; | ||
case 'scale': | ||
// [sx, 0, 0, sy, 0, 0] | ||
matrix = [transform.data[0], 0, 0, transform.data[1] || transform.data[0], 0, 0]; | ||
break; | ||
case 'rotate': | ||
// [cos(a), sin(a), -sin(a), cos(a), x, y] | ||
var cos = mth.cos(transform.data[0]), | ||
sin = mth.sin(transform.data[0]), | ||
cx = transform.data[1] || 0, | ||
cy = transform.data[2] || 0; | ||
matrix = [ | ||
cos, | ||
sin, | ||
-sin, | ||
cos, | ||
(1 - cos) * cx + sin * cy, | ||
(1 - cos) * cy - sin * cx, | ||
]; | ||
break; | ||
case 'skewX': | ||
// [1, 0, tan(a), 1, 0, 0] | ||
matrix = [1, 0, mth.tan(transform.data[0]), 1, 0, 0]; | ||
break; | ||
case 'skewY': | ||
// [1, tan(a), 0, 1, 0, 0] | ||
matrix = [1, mth.tan(transform.data[0]), 0, 1, 0, 0]; | ||
break; | ||
} | ||
matrix = [cos, sin, -sin, cos, (1 - cos) * cx + sin * cy, (1 - cos) * cy - sin * cx]; | ||
break; | ||
case 'skewX': | ||
// [1, 0, tan(a), 1, 0, 0] | ||
matrix = [1, 0, mth.tan(transform.data[0]), 1, 0, 0]; | ||
break; | ||
case 'skewY': | ||
// [1, tan(a), 0, 1, 0, 0] | ||
matrix = [1, mth.tan(transform.data[0]), 0, 1, 0, 0]; | ||
break; | ||
} | ||
return matrix; | ||
return matrix; | ||
} | ||
@@ -250,47 +273,52 @@ | ||
*/ | ||
exports.transformArc = function(arc, transform) { | ||
exports.transformArc = function (arc, transform) { | ||
var a = arc[0], | ||
b = arc[1], | ||
rot = (arc[2] * Math.PI) / 180, | ||
cos = Math.cos(rot), | ||
sin = Math.sin(rot), | ||
h = | ||
Math.pow(arc[5] * cos + arc[6] * sin, 2) / (4 * a * a) + | ||
Math.pow(arc[6] * cos - arc[5] * sin, 2) / (4 * b * b); | ||
if (h > 1) { | ||
h = Math.sqrt(h); | ||
a *= h; | ||
b *= h; | ||
} | ||
var ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0], | ||
m = multiplyTransformMatrices(transform, ellipse), | ||
// Decompose the new ellipse matrix | ||
lastCol = m[2] * m[2] + m[3] * m[3], | ||
squareSum = m[0] * m[0] + m[1] * m[1] + lastCol, | ||
root = | ||
Math.hypot(m[0] - m[3], m[1] + m[2]) * | ||
Math.hypot(m[0] + m[3], m[1] - m[2]); | ||
var a = arc[0], | ||
b = arc[1], | ||
rot = arc[2] * Math.PI / 180, | ||
cos = Math.cos(rot), | ||
sin = Math.sin(rot), | ||
h = Math.pow(arc[5] * cos + arc[6] * sin, 2) / (4 * a * a) + | ||
Math.pow(arc[6] * cos - arc[5] * sin, 2) / (4 * b * b); | ||
if (h > 1) { | ||
h = Math.sqrt(h); | ||
a *= h; | ||
b *= h; | ||
} | ||
var ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0], | ||
m = multiplyTransformMatrices(transform, ellipse), | ||
// Decompose the new ellipse matrix | ||
lastCol = m[2] * m[2] + m[3] * m[3], | ||
squareSum = m[0] * m[0] + m[1] * m[1] + lastCol, | ||
root = Math.hypot(m[0] - m[3], m[1] + m[2]) * Math.hypot(m[0] + m[3], m[1] - m[2]); | ||
if (!root) { | ||
// circle | ||
arc[0] = arc[1] = Math.sqrt(squareSum / 2); | ||
arc[2] = 0; | ||
} else { | ||
var majorAxisSqr = (squareSum + root) / 2, | ||
minorAxisSqr = (squareSum - root) / 2, | ||
major = Math.abs(majorAxisSqr - lastCol) > 1e-6, | ||
sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol, | ||
rowsSum = m[0] * m[2] + m[1] * m[3], | ||
term1 = m[0] * sub + m[2] * rowsSum, | ||
term2 = m[1] * sub + m[3] * rowsSum; | ||
arc[0] = Math.sqrt(majorAxisSqr); | ||
arc[1] = Math.sqrt(minorAxisSqr); | ||
arc[2] = | ||
(((major ? term2 < 0 : term1 > 0) ? -1 : 1) * | ||
Math.acos((major ? term1 : term2) / Math.hypot(term1, term2)) * | ||
180) / | ||
Math.PI; | ||
} | ||
if (!root) { // circle | ||
arc[0] = arc[1] = Math.sqrt(squareSum / 2); | ||
arc[2] = 0; | ||
} else { | ||
var majorAxisSqr = (squareSum + root) / 2, | ||
minorAxisSqr = (squareSum - root) / 2, | ||
major = Math.abs(majorAxisSqr - lastCol) > 1e-6, | ||
sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol, | ||
rowsSum = m[0] * m[2] + m[1] * m[3], | ||
term1 = m[0] * sub + m[2] * rowsSum, | ||
term2 = m[1] * sub + m[3] * rowsSum; | ||
arc[0] = Math.sqrt(majorAxisSqr); | ||
arc[1] = Math.sqrt(minorAxisSqr); | ||
arc[2] = ((major ? term2 < 0 : term1 > 0) ? -1 : 1) * | ||
Math.acos((major ? term1 : term2) / Math.hypot(term1, term2)) * 180 / Math.PI; | ||
} | ||
if (transform[0] < 0 !== transform[3] < 0) { | ||
// Flip the sweep flag if coordinates are being flipped horizontally XOR vertically | ||
arc[4] = 1 - arc[4]; | ||
} | ||
if ((transform[0] < 0) !== (transform[3] < 0)) { | ||
// Flip the sweep flag if coordinates are being flipped horizontally XOR vertically | ||
arc[4] = 1 - arc[4]; | ||
} | ||
return arc; | ||
return arc; | ||
}; | ||
@@ -306,12 +334,10 @@ | ||
function multiplyTransformMatrices(a, b) { | ||
return [ | ||
a[0] * b[0] + a[2] * b[1], | ||
a[1] * b[0] + a[3] * b[1], | ||
a[0] * b[2] + a[2] * b[3], | ||
a[1] * b[2] + a[3] * b[3], | ||
a[0] * b[4] + a[2] * b[5] + a[4], | ||
a[1] * b[4] + a[3] * b[5] + a[5] | ||
]; | ||
return [ | ||
a[0] * b[0] + a[2] * b[1], | ||
a[1] * b[0] + a[3] * b[1], | ||
a[0] * b[2] + a[2] * b[3], | ||
a[1] * b[2] + a[3] * b[3], | ||
a[0] * b[4] + a[2] * b[5] + a[4], | ||
a[1] * b[4] + a[3] * b[5] + a[5], | ||
]; | ||
} |
'use strict'; | ||
const { computeStyle } = require('../lib/style.js'); | ||
const { pathElems } = require('./_collections.js'); | ||
const { path2js, js2path, applyTransforms } = require('./_path.js'); | ||
const { cleanupOutData } = require('../lib/svgo/tools'); | ||
exports.type = 'perItem'; | ||
@@ -7,37 +12,31 @@ | ||
exports.description = 'optimizes path data: writes in shorter form, applies transformations'; | ||
exports.description = | ||
'optimizes path data: writes in shorter form, applies transformations'; | ||
exports.params = { | ||
applyTransforms: true, | ||
applyTransformsStroked: true, | ||
makeArcs: { | ||
threshold: 2.5, // coefficient of rounding error | ||
tolerance: 0.5 // percentage of radius | ||
}, | ||
straightCurves: true, | ||
lineShorthands: true, | ||
curveSmoothShorthands: true, | ||
floatPrecision: 3, | ||
transformPrecision: 5, | ||
removeUseless: true, | ||
collapseRepeated: true, | ||
utilizeAbsolute: true, | ||
leadingZero: true, | ||
negativeExtraSpace: true, | ||
noSpaceAfterFlags: false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
forceAbsolutePath: false, | ||
applyTransforms: true, | ||
applyTransformsStroked: true, | ||
makeArcs: { | ||
threshold: 2.5, // coefficient of rounding error | ||
tolerance: 0.5, // percentage of radius | ||
}, | ||
straightCurves: true, | ||
lineShorthands: true, | ||
curveSmoothShorthands: true, | ||
floatPrecision: 3, | ||
transformPrecision: 5, | ||
removeUseless: true, | ||
collapseRepeated: true, | ||
utilizeAbsolute: true, | ||
leadingZero: true, | ||
negativeExtraSpace: true, | ||
noSpaceAfterFlags: false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
forceAbsolutePath: false, | ||
}; | ||
var pathElems = require('./_collections.js').pathElems, | ||
path2js = require('./_path.js').path2js, | ||
js2path = require('./_path.js').js2path, | ||
applyTransforms = require('./_path.js').applyTransforms, | ||
cleanupOutData = require('../lib/svgo/tools').cleanupOutData, | ||
roundData, | ||
precision, | ||
error, | ||
arcThreshold, | ||
arcTolerance, | ||
hasMarkerMid, | ||
hasStrokeLinecap; | ||
let roundData; | ||
let precision; | ||
let error; | ||
let arcThreshold; | ||
let arcTolerance; | ||
@@ -60,42 +59,47 @@ /** | ||
*/ | ||
exports.fn = function(item, params) { | ||
exports.fn = function (item, params) { | ||
if (item.isElem(pathElems) && item.hasAttr('d')) { | ||
const computedStyle = computeStyle(item); | ||
precision = params.floatPrecision; | ||
error = | ||
precision !== false ? +Math.pow(0.1, precision).toFixed(precision) : 1e-2; | ||
roundData = precision > 0 && precision < 20 ? strongRound : round; | ||
if (params.makeArcs) { | ||
arcThreshold = params.makeArcs.threshold; | ||
arcTolerance = params.makeArcs.tolerance; | ||
} | ||
const hasMarkerMid = computedStyle['marker-mid'] != null; | ||
if (item.isElem(pathElems) && item.hasAttr('d')) { | ||
const maybeHasStroke = | ||
computedStyle.stroke && | ||
(computedStyle.stroke.type === 'dynamic' || | ||
computedStyle.stroke.value !== 'none'); | ||
const maybeHasLinecap = | ||
computedStyle['stroke-linecap'] && | ||
(computedStyle['stroke-linecap'].type === 'dynamic' || | ||
computedStyle['stroke-linecap'].value !== 'butt'); | ||
const maybeHasStrokeAndLinecap = maybeHasStroke && maybeHasLinecap; | ||
precision = params.floatPrecision; | ||
error = precision !== false ? +Math.pow(.1, precision).toFixed(precision) : 1e-2; | ||
roundData = precision > 0 && precision < 20 ? strongRound : round; | ||
if (params.makeArcs) { | ||
arcThreshold = params.makeArcs.threshold; | ||
arcTolerance = params.makeArcs.tolerance; | ||
} | ||
hasMarkerMid = item.hasAttr('marker-mid'); | ||
var data = path2js(item); | ||
const stroke = item.computedAttr('stroke'); | ||
const strokeLinecap = item.computedAttr('stroke-linecap'); | ||
// stroke-linecap may exist in inline styles which are not parsed for now | ||
hasStrokeLinecap = item.hasAttr('style') || | ||
stroke && stroke != 'none' && strokeLinecap && strokeLinecap != 'butt'; | ||
// TODO: get rid of functions returns | ||
if (data.length) { | ||
convertToRelative(data); | ||
var data = path2js(item); | ||
if (params.applyTransforms) { | ||
data = applyTransforms(item, data, params); | ||
} | ||
// TODO: get rid of functions returns | ||
if (data.length) { | ||
convertToRelative(data); | ||
data = filters(data, params, { | ||
maybeHasStrokeAndLinecap, | ||
hasMarkerMid, | ||
}); | ||
if (params.applyTransforms) { | ||
data = applyTransforms(item, data, params); | ||
} | ||
if (params.utilizeAbsolute) { | ||
data = convertToMixed(data, params); | ||
} | ||
data = filters(data, params); | ||
if (params.utilizeAbsolute) { | ||
data = convertToMixed(data, params); | ||
} | ||
js2path(item, data, params); | ||
} | ||
js2path(item, data, params); | ||
} | ||
} | ||
}; | ||
@@ -111,158 +115,137 @@ | ||
function convertToRelative(path) { | ||
var point = [0, 0], | ||
subpathPoint = [0, 0], | ||
baseItem; | ||
var point = [0, 0], | ||
subpathPoint = [0, 0], | ||
baseItem; | ||
path.forEach(function (item, index) { | ||
var instruction = item.instruction, | ||
data = item.data; | ||
path.forEach(function(item, index) { | ||
// data !== !z | ||
if (data) { | ||
// already relative | ||
// recalculate current point | ||
if ('mcslqta'.indexOf(instruction) > -1) { | ||
point[0] += data[data.length - 2]; | ||
point[1] += data[data.length - 1]; | ||
var instruction = item.instruction, | ||
data = item.data; | ||
if (instruction === 'm') { | ||
subpathPoint[0] = point[0]; | ||
subpathPoint[1] = point[1]; | ||
baseItem = item; | ||
} | ||
} else if (instruction === 'h') { | ||
point[0] += data[0]; | ||
} else if (instruction === 'v') { | ||
point[1] += data[0]; | ||
} | ||
// data !== !z | ||
if (data) { | ||
// convert absolute path data coordinates to relative | ||
// if "M" was not transformed from "m" | ||
// M → m | ||
if (instruction === 'M') { | ||
if (index > 0) instruction = 'm'; | ||
// already relative | ||
// recalculate current point | ||
if ('mcslqta'.indexOf(instruction) > -1) { | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
point[0] += data[data.length - 2]; | ||
point[1] += data[data.length - 1]; | ||
subpathPoint[0] = point[0] += data[0]; | ||
subpathPoint[1] = point[1] += data[1]; | ||
if (instruction === 'm') { | ||
subpathPoint[0] = point[0]; | ||
subpathPoint[1] = point[1]; | ||
baseItem = item; | ||
} | ||
baseItem = item; | ||
} | ||
} else if (instruction === 'h') { | ||
// L → l | ||
// T → t | ||
else if ('LT'.indexOf(instruction) > -1) { | ||
instruction = instruction.toLowerCase(); | ||
point[0] += data[0]; | ||
// x y | ||
// 0 1 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
} else if (instruction === 'v') { | ||
point[0] += data[0]; | ||
point[1] += data[1]; | ||
point[1] += data[0]; | ||
// C → c | ||
} else if (instruction === 'C') { | ||
instruction = 'c'; | ||
} | ||
// x1 y1 x2 y2 x y | ||
// 0 1 2 3 4 5 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
data[4] -= point[0]; | ||
data[5] -= point[1]; | ||
// convert absolute path data coordinates to relative | ||
// if "M" was not transformed from "m" | ||
// M → m | ||
if (instruction === 'M') { | ||
point[0] += data[4]; | ||
point[1] += data[5]; | ||
if (index > 0) instruction = 'm'; | ||
// S → s | ||
// Q → q | ||
} else if ('SQ'.indexOf(instruction) > -1) { | ||
instruction = instruction.toLowerCase(); | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
// x1 y1 x y | ||
// 0 1 2 3 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
subpathPoint[0] = point[0] += data[0]; | ||
subpathPoint[1] = point[1] += data[1]; | ||
point[0] += data[2]; | ||
point[1] += data[3]; | ||
baseItem = item; | ||
// A → a | ||
} else if (instruction === 'A') { | ||
instruction = 'a'; | ||
} | ||
// rx ry x-axis-rotation large-arc-flag sweep-flag x y | ||
// 0 1 2 3 4 5 6 | ||
data[5] -= point[0]; | ||
data[6] -= point[1]; | ||
// L → l | ||
// T → t | ||
else if ('LT'.indexOf(instruction) > -1) { | ||
point[0] += data[5]; | ||
point[1] += data[6]; | ||
instruction = instruction.toLowerCase(); | ||
// H → h | ||
} else if (instruction === 'H') { | ||
instruction = 'h'; | ||
// x y | ||
// 0 1 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[0] -= point[0]; | ||
point[0] += data[0]; | ||
point[1] += data[1]; | ||
point[0] += data[0]; | ||
// C → c | ||
} else if (instruction === 'C') { | ||
// V → v | ||
} else if (instruction === 'V') { | ||
instruction = 'v'; | ||
instruction = 'c'; | ||
data[0] -= point[1]; | ||
// x1 y1 x2 y2 x y | ||
// 0 1 2 3 4 5 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
data[4] -= point[0]; | ||
data[5] -= point[1]; | ||
point[1] += data[0]; | ||
} | ||
point[0] += data[4]; | ||
point[1] += data[5]; | ||
item.instruction = instruction; | ||
item.data = data; | ||
// S → s | ||
// Q → q | ||
} else if ('SQ'.indexOf(instruction) > -1) { | ||
// store absolute coordinates for later use | ||
item.coords = point.slice(-2); | ||
} | ||
instruction = instruction.toLowerCase(); | ||
// !data === z, reset current point | ||
else if (instruction == 'z') { | ||
if (baseItem) { | ||
item.coords = baseItem.coords; | ||
} | ||
point[0] = subpathPoint[0]; | ||
point[1] = subpathPoint[1]; | ||
} | ||
// x1 y1 x y | ||
// 0 1 2 3 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
item.base = index > 0 ? path[index - 1].coords : [0, 0]; | ||
}); | ||
point[0] += data[2]; | ||
point[1] += data[3]; | ||
// A → a | ||
} else if (instruction === 'A') { | ||
instruction = 'a'; | ||
// rx ry x-axis-rotation large-arc-flag sweep-flag x y | ||
// 0 1 2 3 4 5 6 | ||
data[5] -= point[0]; | ||
data[6] -= point[1]; | ||
point[0] += data[5]; | ||
point[1] += data[6]; | ||
// H → h | ||
} else if (instruction === 'H') { | ||
instruction = 'h'; | ||
data[0] -= point[0]; | ||
point[0] += data[0]; | ||
// V → v | ||
} else if (instruction === 'V') { | ||
instruction = 'v'; | ||
data[0] -= point[1]; | ||
point[1] += data[0]; | ||
} | ||
item.instruction = instruction; | ||
item.data = data; | ||
// store absolute coordinates for later use | ||
item.coords = point.slice(-2); | ||
} | ||
// !data === z, reset current point | ||
else if (instruction == 'z') { | ||
if (baseItem) { | ||
item.coords = baseItem.coords; | ||
} | ||
point[0] = subpathPoint[0]; | ||
point[1] = subpathPoint[1]; | ||
} | ||
item.base = index > 0 ? path[index - 1].coords : [0, 0]; | ||
}); | ||
return path; | ||
return path; | ||
} | ||
@@ -277,370 +260,356 @@ | ||
*/ | ||
function filters(path, params) { | ||
function filters(path, params, { maybeHasStrokeAndLinecap, hasMarkerMid }) { | ||
var stringify = data2Path.bind(null, params), | ||
relSubpoint = [0, 0], | ||
pathBase = [0, 0], | ||
prev = {}; | ||
var stringify = data2Path.bind(null, params), | ||
relSubpoint = [0, 0], | ||
pathBase = [0, 0], | ||
prev = {}; | ||
path = path.filter(function (item, index, path) { | ||
var instruction = item.instruction, | ||
data = item.data, | ||
next = path[index + 1]; | ||
path = path.filter(function(item, index, path) { | ||
if (data) { | ||
var sdata = data, | ||
circle; | ||
var instruction = item.instruction, | ||
data = item.data, | ||
next = path[index + 1]; | ||
if (instruction === 's') { | ||
sdata = [0, 0].concat(data); | ||
if (data) { | ||
if ('cs'.indexOf(prev.instruction) > -1) { | ||
var pdata = prev.data, | ||
n = pdata.length; | ||
var sdata = data, | ||
circle; | ||
// (-x, -y) of the prev tangent point relative to the current point | ||
sdata[0] = pdata[n - 2] - pdata[n - 4]; | ||
sdata[1] = pdata[n - 1] - pdata[n - 3]; | ||
} | ||
} | ||
if (instruction === 's') { | ||
sdata = [0, 0].concat(data); | ||
// convert curves to arcs if possible | ||
if ( | ||
params.makeArcs && | ||
(instruction == 'c' || instruction == 's') && | ||
isConvex(sdata) && | ||
(circle = findCircle(sdata)) | ||
) { | ||
var r = roundData([circle.radius])[0], | ||
angle = findArcAngle(sdata, circle), | ||
sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0, | ||
arc = { | ||
instruction: 'a', | ||
data: [r, r, 0, 0, sweep, sdata[4], sdata[5]], | ||
coords: item.coords.slice(), | ||
base: item.base, | ||
}, | ||
output = [arc], | ||
// relative coordinates to adjust the found circle | ||
relCenter = [ | ||
circle.center[0] - sdata[4], | ||
circle.center[1] - sdata[5], | ||
], | ||
relCircle = { center: relCenter, radius: circle.radius }, | ||
arcCurves = [item], | ||
hasPrev = 0, | ||
suffix = '', | ||
nextLonghand; | ||
if ('cs'.indexOf(prev.instruction) > -1) { | ||
var pdata = prev.data, | ||
n = pdata.length; | ||
if ( | ||
(prev.instruction == 'c' && | ||
isConvex(prev.data) && | ||
isArcPrev(prev.data, circle)) || | ||
(prev.instruction == 'a' && | ||
prev.sdata && | ||
isArcPrev(prev.sdata, circle)) | ||
) { | ||
arcCurves.unshift(prev); | ||
arc.base = prev.base; | ||
arc.data[5] = arc.coords[0] - arc.base[0]; | ||
arc.data[6] = arc.coords[1] - arc.base[1]; | ||
var prevData = prev.instruction == 'a' ? prev.sdata : prev.data; | ||
var prevAngle = findArcAngle(prevData, { | ||
center: [ | ||
prevData[4] + circle.center[0], | ||
prevData[5] + circle.center[1], | ||
], | ||
radius: circle.radius, | ||
}); | ||
angle += prevAngle; | ||
if (angle > Math.PI) arc.data[3] = 1; | ||
hasPrev = 1; | ||
} | ||
// (-x, -y) of the prev tangent point relative to the current point | ||
sdata[0] = pdata[n - 2] - pdata[n - 4]; | ||
sdata[1] = pdata[n - 1] - pdata[n - 3]; | ||
} | ||
// check if next curves are fitting the arc | ||
for ( | ||
var j = index; | ||
(next = path[++j]) && ~'cs'.indexOf(next.instruction); | ||
) { | ||
var nextData = next.data; | ||
if (next.instruction == 's') { | ||
nextLonghand = makeLonghand( | ||
{ instruction: 's', data: next.data.slice() }, | ||
path[j - 1].data | ||
); | ||
nextData = nextLonghand.data; | ||
nextLonghand.data = nextData.slice(0, 2); | ||
suffix = stringify([nextLonghand]); | ||
} | ||
if (isConvex(nextData) && isArc(nextData, relCircle)) { | ||
angle += findArcAngle(nextData, relCircle); | ||
if (angle - 2 * Math.PI > 1e-3) break; // more than 360° | ||
if (angle > Math.PI) arc.data[3] = 1; | ||
arcCurves.push(next); | ||
if (2 * Math.PI - angle > 1e-3) { | ||
// less than 360° | ||
arc.coords = next.coords; | ||
arc.data[5] = arc.coords[0] - arc.base[0]; | ||
arc.data[6] = arc.coords[1] - arc.base[1]; | ||
} else { | ||
// full circle, make a half-circle arc and add a second one | ||
arc.data[5] = 2 * (relCircle.center[0] - nextData[4]); | ||
arc.data[6] = 2 * (relCircle.center[1] - nextData[5]); | ||
arc.coords = [ | ||
arc.base[0] + arc.data[5], | ||
arc.base[1] + arc.data[6], | ||
]; | ||
arc = { | ||
instruction: 'a', | ||
data: [ | ||
r, | ||
r, | ||
0, | ||
0, | ||
sweep, | ||
next.coords[0] - arc.coords[0], | ||
next.coords[1] - arc.coords[1], | ||
], | ||
coords: next.coords, | ||
base: arc.coords, | ||
}; | ||
output.push(arc); | ||
j++; | ||
break; | ||
} | ||
relCenter[0] -= nextData[4]; | ||
relCenter[1] -= nextData[5]; | ||
} else break; | ||
} | ||
// convert curves to arcs if possible | ||
if ( | ||
params.makeArcs && | ||
(instruction == 'c' || instruction == 's') && | ||
isConvex(sdata) && | ||
(circle = findCircle(sdata)) | ||
) { | ||
var r = roundData([circle.radius])[0], | ||
angle = findArcAngle(sdata, circle), | ||
sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0, | ||
arc = { | ||
instruction: 'a', | ||
data: [r, r, 0, 0, sweep, sdata[4], sdata[5]], | ||
coords: item.coords.slice(), | ||
base: item.base | ||
}, | ||
output = [arc], | ||
// relative coordinates to adjust the found circle | ||
relCenter = [circle.center[0] - sdata[4], circle.center[1] - sdata[5]], | ||
relCircle = { center: relCenter, radius: circle.radius }, | ||
arcCurves = [item], | ||
hasPrev = 0, | ||
suffix = '', | ||
nextLonghand; | ||
if ((stringify(output) + suffix).length < stringify(arcCurves).length) { | ||
if (path[j] && path[j].instruction == 's') { | ||
makeLonghand(path[j], path[j - 1].data); | ||
} | ||
if (hasPrev) { | ||
var prevArc = output.shift(); | ||
roundData(prevArc.data); | ||
relSubpoint[0] += prevArc.data[5] - prev.data[prev.data.length - 2]; | ||
relSubpoint[1] += prevArc.data[6] - prev.data[prev.data.length - 1]; | ||
prev.instruction = 'a'; | ||
prev.data = prevArc.data; | ||
item.base = prev.coords = prevArc.coords; | ||
} | ||
arc = output.shift(); | ||
if (arcCurves.length == 1) { | ||
item.sdata = sdata.slice(); // preserve curve data for future checks | ||
} else if (arcCurves.length - 1 - hasPrev > 0) { | ||
// filter out consumed next items | ||
path.splice.apply( | ||
path, | ||
[index + 1, arcCurves.length - 1 - hasPrev].concat(output) | ||
); | ||
} | ||
if (!arc) return false; | ||
instruction = 'a'; | ||
data = arc.data; | ||
item.coords = arc.coords; | ||
} | ||
} | ||
if ( | ||
prev.instruction == 'c' && isConvex(prev.data) && isArcPrev(prev.data, circle) || | ||
prev.instruction == 'a' && prev.sdata && isArcPrev(prev.sdata, circle) | ||
) { | ||
arcCurves.unshift(prev); | ||
arc.base = prev.base; | ||
arc.data[5] = arc.coords[0] - arc.base[0]; | ||
arc.data[6] = arc.coords[1] - arc.base[1]; | ||
var prevData = prev.instruction == 'a' ? prev.sdata : prev.data; | ||
var prevAngle = findArcAngle(prevData, | ||
{ | ||
center: [prevData[4] + circle.center[0], prevData[5] + circle.center[1]], | ||
radius: circle.radius | ||
} | ||
); | ||
angle += prevAngle; | ||
if (angle > Math.PI) arc.data[3] = 1; | ||
hasPrev = 1; | ||
} | ||
// Rounding relative coordinates, taking in account accummulating error | ||
// to get closer to absolute coordinates. Sum of rounded value remains same: | ||
// l .25 3 .25 2 .25 3 .25 2 -> l .3 3 .2 2 .3 3 .2 2 | ||
if (precision !== false) { | ||
if ('mltqsc'.indexOf(instruction) > -1) { | ||
for (var i = data.length; i--; ) { | ||
data[i] += item.base[i % 2] - relSubpoint[i % 2]; | ||
} | ||
} else if (instruction == 'h') { | ||
data[0] += item.base[0] - relSubpoint[0]; | ||
} else if (instruction == 'v') { | ||
data[0] += item.base[1] - relSubpoint[1]; | ||
} else if (instruction == 'a') { | ||
data[5] += item.base[0] - relSubpoint[0]; | ||
data[6] += item.base[1] - relSubpoint[1]; | ||
} | ||
roundData(data); | ||
// check if next curves are fitting the arc | ||
for (var j = index; (next = path[++j]) && ~'cs'.indexOf(next.instruction);) { | ||
var nextData = next.data; | ||
if (next.instruction == 's') { | ||
nextLonghand = makeLonghand({instruction: 's', data: next.data.slice() }, | ||
path[j - 1].data); | ||
nextData = nextLonghand.data; | ||
nextLonghand.data = nextData.slice(0, 2); | ||
suffix = stringify([nextLonghand]); | ||
} | ||
if (isConvex(nextData) && isArc(nextData, relCircle)) { | ||
angle += findArcAngle(nextData, relCircle); | ||
if (angle - 2 * Math.PI > 1e-3) break; // more than 360° | ||
if (angle > Math.PI) arc.data[3] = 1; | ||
arcCurves.push(next); | ||
if (2 * Math.PI - angle > 1e-3) { // less than 360° | ||
arc.coords = next.coords; | ||
arc.data[5] = arc.coords[0] - arc.base[0]; | ||
arc.data[6] = arc.coords[1] - arc.base[1]; | ||
} else { | ||
// full circle, make a half-circle arc and add a second one | ||
arc.data[5] = 2 * (relCircle.center[0] - nextData[4]); | ||
arc.data[6] = 2 * (relCircle.center[1] - nextData[5]); | ||
arc.coords = [arc.base[0] + arc.data[5], arc.base[1] + arc.data[6]]; | ||
arc = { | ||
instruction: 'a', | ||
data: [r, r, 0, 0, sweep, | ||
next.coords[0] - arc.coords[0], next.coords[1] - arc.coords[1]], | ||
coords: next.coords, | ||
base: arc.coords | ||
}; | ||
output.push(arc); | ||
j++; | ||
break; | ||
} | ||
relCenter[0] -= nextData[4]; | ||
relCenter[1] -= nextData[5]; | ||
} else break; | ||
} | ||
if (instruction == 'h') relSubpoint[0] += data[0]; | ||
else if (instruction == 'v') relSubpoint[1] += data[0]; | ||
else { | ||
relSubpoint[0] += data[data.length - 2]; | ||
relSubpoint[1] += data[data.length - 1]; | ||
} | ||
roundData(relSubpoint); | ||
if ((stringify(output) + suffix).length < stringify(arcCurves).length) { | ||
if (path[j] && path[j].instruction == 's') { | ||
makeLonghand(path[j], path[j - 1].data); | ||
} | ||
if (hasPrev) { | ||
var prevArc = output.shift(); | ||
roundData(prevArc.data); | ||
relSubpoint[0] += prevArc.data[5] - prev.data[prev.data.length - 2]; | ||
relSubpoint[1] += prevArc.data[6] - prev.data[prev.data.length - 1]; | ||
prev.instruction = 'a'; | ||
prev.data = prevArc.data; | ||
item.base = prev.coords = prevArc.coords; | ||
} | ||
arc = output.shift(); | ||
if (arcCurves.length == 1) { | ||
item.sdata = sdata.slice(); // preserve curve data for future checks | ||
} else if (arcCurves.length - 1 - hasPrev > 0) { | ||
// filter out consumed next items | ||
path.splice.apply(path, [index + 1, arcCurves.length - 1 - hasPrev].concat(output)); | ||
} | ||
if (!arc) return false; | ||
instruction = 'a'; | ||
data = arc.data; | ||
item.coords = arc.coords; | ||
} | ||
} | ||
if (instruction.toLowerCase() == 'm') { | ||
pathBase[0] = relSubpoint[0]; | ||
pathBase[1] = relSubpoint[1]; | ||
} | ||
} | ||
// Rounding relative coordinates, taking in account accummulating error | ||
// to get closer to absolute coordinates. Sum of rounded value remains same: | ||
// l .25 3 .25 2 .25 3 .25 2 -> l .3 3 .2 2 .3 3 .2 2 | ||
if (precision !== false) { | ||
if ('mltqsc'.indexOf(instruction) > -1) { | ||
for (var i = data.length; i--;) { | ||
data[i] += item.base[i % 2] - relSubpoint[i % 2]; | ||
} | ||
} else if (instruction == 'h') { | ||
data[0] += item.base[0] - relSubpoint[0]; | ||
} else if (instruction == 'v') { | ||
data[0] += item.base[1] - relSubpoint[1]; | ||
} else if (instruction == 'a') { | ||
data[5] += item.base[0] - relSubpoint[0]; | ||
data[6] += item.base[1] - relSubpoint[1]; | ||
} | ||
roundData(data); | ||
// convert straight curves into lines segments | ||
if (params.straightCurves) { | ||
if ( | ||
(instruction === 'c' && isCurveStraightLine(data)) || | ||
(instruction === 's' && isCurveStraightLine(sdata)) | ||
) { | ||
if (next && next.instruction == 's') makeLonghand(next, data); // fix up next curve | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} else if (instruction === 'q' && isCurveStraightLine(data)) { | ||
if (next && next.instruction == 't') makeLonghand(next, data); // fix up next curve | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} else if ( | ||
instruction === 't' && | ||
prev.instruction !== 'q' && | ||
prev.instruction !== 't' | ||
) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} else if (instruction === 'a' && (data[0] === 0 || data[1] === 0)) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
} | ||
if (instruction == 'h') relSubpoint[0] += data[0]; | ||
else if (instruction == 'v') relSubpoint[1] += data[0]; | ||
else { | ||
relSubpoint[0] += data[data.length - 2]; | ||
relSubpoint[1] += data[data.length - 1]; | ||
} | ||
roundData(relSubpoint); | ||
// horizontal and vertical line shorthands | ||
// l 50 0 → h 50 | ||
// l 0 50 → v 50 | ||
if (params.lineShorthands && instruction === 'l') { | ||
if (data[1] === 0) { | ||
instruction = 'h'; | ||
data.pop(); | ||
} else if (data[0] === 0) { | ||
instruction = 'v'; | ||
data.shift(); | ||
} | ||
} | ||
if (instruction.toLowerCase() == 'm') { | ||
pathBase[0] = relSubpoint[0]; | ||
pathBase[1] = relSubpoint[1]; | ||
} | ||
} | ||
// collapse repeated commands | ||
// h 20 h 30 -> h 50 | ||
if ( | ||
params.collapseRepeated && | ||
hasMarkerMid === false && | ||
'mhv'.indexOf(instruction) > -1 && | ||
prev.instruction && | ||
instruction == prev.instruction.toLowerCase() && | ||
((instruction != 'h' && instruction != 'v') || | ||
prev.data[0] >= 0 == data[0] >= 0) | ||
) { | ||
prev.data[0] += data[0]; | ||
if (instruction != 'h' && instruction != 'v') { | ||
prev.data[1] += data[1]; | ||
} | ||
prev.coords = item.coords; | ||
path[index] = prev; | ||
return false; | ||
} | ||
// convert straight curves into lines segments | ||
if (params.straightCurves) { | ||
// convert curves into smooth shorthands | ||
if (params.curveSmoothShorthands && prev.instruction) { | ||
// curveto | ||
if (instruction === 'c') { | ||
// c + c → c + s | ||
if ( | ||
prev.instruction === 'c' && | ||
data[0] === -(prev.data[2] - prev.data[4]) && | ||
data[1] === -(prev.data[3] - prev.data[5]) | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
if ( | ||
instruction === 'c' && | ||
isCurveStraightLine(data) || | ||
instruction === 's' && | ||
isCurveStraightLine(sdata) | ||
) { | ||
if (next && next.instruction == 's') | ||
makeLonghand(next, data); // fix up next curve | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
// s + c → s + s | ||
else if ( | ||
prev.instruction === 's' && | ||
data[0] === -(prev.data[0] - prev.data[2]) && | ||
data[1] === -(prev.data[1] - prev.data[3]) | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
else if ( | ||
instruction === 'q' && | ||
isCurveStraightLine(data) | ||
) { | ||
if (next && next.instruction == 't') | ||
makeLonghand(next, data); // fix up next curve | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
// [^cs] + c → [^cs] + s | ||
else if ( | ||
'cs'.indexOf(prev.instruction) === -1 && | ||
data[0] === 0 && | ||
data[1] === 0 | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
} | ||
else if ( | ||
instruction === 't' && | ||
prev.instruction !== 'q' && | ||
prev.instruction !== 't' | ||
) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
// quadratic Bézier curveto | ||
else if (instruction === 'q') { | ||
// q + q → q + t | ||
if ( | ||
prev.instruction === 'q' && | ||
data[0] === prev.data[2] - prev.data[0] && | ||
data[1] === prev.data[3] - prev.data[1] | ||
) { | ||
instruction = 't'; | ||
data = data.slice(2); | ||
} | ||
else if ( | ||
instruction === 'a' && | ||
(data[0] === 0 || data[1] === 0) | ||
) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
} | ||
// t + q → t + t | ||
else if ( | ||
prev.instruction === 't' && | ||
data[2] === prev.data[0] && | ||
data[3] === prev.data[1] | ||
) { | ||
instruction = 't'; | ||
data = data.slice(2); | ||
} | ||
} | ||
} | ||
// horizontal and vertical line shorthands | ||
// l 50 0 → h 50 | ||
// l 0 50 → v 50 | ||
if ( | ||
params.lineShorthands && | ||
instruction === 'l' | ||
) { | ||
if (data[1] === 0) { | ||
instruction = 'h'; | ||
data.pop(); | ||
} else if (data[0] === 0) { | ||
instruction = 'v'; | ||
data.shift(); | ||
} | ||
} | ||
// remove useless non-first path segments | ||
if (params.removeUseless && !maybeHasStrokeAndLinecap) { | ||
// l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0 | ||
if ( | ||
'lhvqtcs'.indexOf(instruction) > -1 && | ||
data.every(function (i) { | ||
return i === 0; | ||
}) | ||
) { | ||
path[index] = prev; | ||
return false; | ||
} | ||
// collapse repeated commands | ||
// h 20 h 30 -> h 50 | ||
if ( | ||
params.collapseRepeated && | ||
!hasMarkerMid && | ||
('mhv'.indexOf(instruction) > -1) && | ||
prev.instruction && | ||
instruction == prev.instruction.toLowerCase() && | ||
( | ||
(instruction != 'h' && instruction != 'v') || | ||
(prev.data[0] >= 0) == (data[0] >= 0) | ||
)) { | ||
prev.data[0] += data[0]; | ||
if (instruction != 'h' && instruction != 'v') { | ||
prev.data[1] += data[1]; | ||
} | ||
prev.coords = item.coords; | ||
path[index] = prev; | ||
return false; | ||
} | ||
// convert curves into smooth shorthands | ||
if (params.curveSmoothShorthands && prev.instruction) { | ||
// curveto | ||
if (instruction === 'c') { | ||
// c + c → c + s | ||
if ( | ||
prev.instruction === 'c' && | ||
data[0] === -(prev.data[2] - prev.data[4]) && | ||
data[1] === -(prev.data[3] - prev.data[5]) | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
// s + c → s + s | ||
else if ( | ||
prev.instruction === 's' && | ||
data[0] === -(prev.data[0] - prev.data[2]) && | ||
data[1] === -(prev.data[1] - prev.data[3]) | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
// [^cs] + c → [^cs] + s | ||
else if ( | ||
'cs'.indexOf(prev.instruction) === -1 && | ||
data[0] === 0 && | ||
data[1] === 0 | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
} | ||
// quadratic Bézier curveto | ||
else if (instruction === 'q') { | ||
// q + q → q + t | ||
if ( | ||
prev.instruction === 'q' && | ||
data[0] === (prev.data[2] - prev.data[0]) && | ||
data[1] === (prev.data[3] - prev.data[1]) | ||
) { | ||
instruction = 't'; | ||
data = data.slice(2); | ||
} | ||
// t + q → t + t | ||
else if ( | ||
prev.instruction === 't' && | ||
data[2] === prev.data[0] && | ||
data[3] === prev.data[1] | ||
) { | ||
instruction = 't'; | ||
data = data.slice(2); | ||
} | ||
} | ||
} | ||
// remove useless non-first path segments | ||
if (params.removeUseless && !hasStrokeLinecap) { | ||
// l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0 | ||
if ( | ||
( | ||
'lhvqtcs'.indexOf(instruction) > -1 | ||
) && | ||
data.every(function(i) { return i === 0; }) | ||
) { | ||
path[index] = prev; | ||
return false; | ||
} | ||
// a 25,25 -30 0,1 0,0 | ||
if ( | ||
instruction === 'a' && | ||
data[5] === 0 && | ||
data[6] === 0 | ||
) { | ||
path[index] = prev; | ||
return false; | ||
} | ||
} | ||
item.instruction = instruction; | ||
item.data = data; | ||
prev = item; | ||
} else { | ||
// z resets coordinates | ||
relSubpoint[0] = pathBase[0]; | ||
relSubpoint[1] = pathBase[1]; | ||
if (prev.instruction == 'z') return false; | ||
prev = item; | ||
// a 25,25 -30 0,1 0,0 | ||
if (instruction === 'a' && data[5] === 0 && data[6] === 0) { | ||
path[index] = prev; | ||
return false; | ||
} | ||
} | ||
return true; | ||
item.instruction = instruction; | ||
item.data = data; | ||
}); | ||
prev = item; | ||
} else { | ||
// z resets coordinates | ||
relSubpoint[0] = pathBase[0]; | ||
relSubpoint[1] = pathBase[1]; | ||
if (prev.instruction == 'z') return false; | ||
prev = item; | ||
} | ||
return path; | ||
return true; | ||
}); | ||
return path; | ||
} | ||
@@ -655,62 +624,59 @@ | ||
function convertToMixed(path, params) { | ||
var prev = path[0]; | ||
var prev = path[0]; | ||
path = path.filter(function (item, index) { | ||
if (index == 0) return true; | ||
if (!item.data) { | ||
prev = item; | ||
return true; | ||
} | ||
path = path.filter(function(item, index) { | ||
var instruction = item.instruction, | ||
data = item.data, | ||
adata = data && data.slice(0); | ||
if (index == 0) return true; | ||
if (!item.data) { | ||
prev = item; | ||
return true; | ||
} | ||
if ('mltqsc'.indexOf(instruction) > -1) { | ||
for (var i = adata.length; i--; ) { | ||
adata[i] += item.base[i % 2]; | ||
} | ||
} else if (instruction == 'h') { | ||
adata[0] += item.base[0]; | ||
} else if (instruction == 'v') { | ||
adata[0] += item.base[1]; | ||
} else if (instruction == 'a') { | ||
adata[5] += item.base[0]; | ||
adata[6] += item.base[1]; | ||
} | ||
var instruction = item.instruction, | ||
data = item.data, | ||
adata = data && data.slice(0); | ||
roundData(adata); | ||
if ('mltqsc'.indexOf(instruction) > -1) { | ||
for (var i = adata.length; i--;) { | ||
adata[i] += item.base[i % 2]; | ||
} | ||
} else if (instruction == 'h') { | ||
adata[0] += item.base[0]; | ||
} else if (instruction == 'v') { | ||
adata[0] += item.base[1]; | ||
} else if (instruction == 'a') { | ||
adata[5] += item.base[0]; | ||
adata[6] += item.base[1]; | ||
} | ||
var absoluteDataStr = cleanupOutData(adata, params), | ||
relativeDataStr = cleanupOutData(data, params); | ||
roundData(adata); | ||
// Convert to absolute coordinates if it's shorter or forceAbsolutePath is true. | ||
// v-20 -> V0 | ||
// Don't convert if it fits following previous instruction. | ||
// l20 30-10-50 instead of l20 30L20 30 | ||
if ( | ||
params.forceAbsolutePath || | ||
(absoluteDataStr.length < relativeDataStr.length && | ||
!( | ||
params.negativeExtraSpace && | ||
instruction == prev.instruction && | ||
prev.instruction.charCodeAt(0) > 96 && | ||
absoluteDataStr.length == relativeDataStr.length - 1 && | ||
(data[0] < 0 || | ||
(/^0\./.test(data[0]) && prev.data[prev.data.length - 1] % 1)) | ||
)) | ||
) { | ||
item.instruction = instruction.toUpperCase(); | ||
item.data = adata; | ||
} | ||
var absoluteDataStr = cleanupOutData(adata, params), | ||
relativeDataStr = cleanupOutData(data, params); | ||
prev = item; | ||
// Convert to absolute coordinates if it's shorter or forceAbsolutePath is true. | ||
// v-20 -> V0 | ||
// Don't convert if it fits following previous instruction. | ||
// l20 30-10-50 instead of l20 30L20 30 | ||
if ( | ||
params.forceAbsolutePath || ( | ||
absoluteDataStr.length < relativeDataStr.length && | ||
!( | ||
params.negativeExtraSpace && | ||
instruction == prev.instruction && | ||
prev.instruction.charCodeAt(0) > 96 && | ||
absoluteDataStr.length == relativeDataStr.length - 1 && | ||
(data[0] < 0 || /^0\./.test(data[0]) && prev.data[prev.data.length - 1] % 1) | ||
)) | ||
) { | ||
item.instruction = instruction.toUpperCase(); | ||
item.data = adata; | ||
} | ||
return true; | ||
}); | ||
prev = item; | ||
return true; | ||
}); | ||
return path; | ||
return path; | ||
} | ||
@@ -726,11 +692,20 @@ | ||
function isConvex(data) { | ||
var center = getIntersection([ | ||
0, | ||
0, | ||
data[2], | ||
data[3], | ||
data[0], | ||
data[1], | ||
data[4], | ||
data[5], | ||
]); | ||
var center = getIntersection([0, 0, data[2], data[3], data[0], data[1], data[4], data[5]]); | ||
return center && | ||
(data[2] < center[0] == center[0] < 0) && | ||
(data[3] < center[1] == center[1] < 0) && | ||
(data[4] < center[0] == center[0] < data[0]) && | ||
(data[5] < center[1] == center[1] < data[1]); | ||
return ( | ||
center && | ||
data[2] < center[0] == center[0] < 0 && | ||
data[3] < center[1] == center[1] < 0 && | ||
data[4] < center[0] == center[0] < data[0] && | ||
data[5] < center[1] == center[1] < data[1] | ||
); | ||
} | ||
@@ -745,27 +720,23 @@ | ||
function getIntersection(coords) { | ||
// Prev line equation parameters. | ||
var a1 = coords[1] - coords[3], // y1 - y2 | ||
b1 = coords[2] - coords[0], // x2 - x1 | ||
c1 = coords[0] * coords[3] - coords[2] * coords[1], // x1 * y2 - x2 * y1 | ||
// Next line equation parameters | ||
a2 = coords[5] - coords[7], // y1 - y2 | ||
b2 = coords[6] - coords[4], // x2 - x1 | ||
c2 = coords[4] * coords[7] - coords[5] * coords[6], // x1 * y2 - x2 * y1 | ||
denom = a1 * b2 - a2 * b1; | ||
// Prev line equation parameters. | ||
var a1 = coords[1] - coords[3], // y1 - y2 | ||
b1 = coords[2] - coords[0], // x2 - x1 | ||
c1 = coords[0] * coords[3] - coords[2] * coords[1], // x1 * y2 - x2 * y1 | ||
if (!denom) return; // parallel lines havn't an intersection | ||
// Next line equation parameters | ||
a2 = coords[5] - coords[7], // y1 - y2 | ||
b2 = coords[6] - coords[4], // x2 - x1 | ||
c2 = coords[4] * coords[7] - coords[5] * coords[6], // x1 * y2 - x2 * y1 | ||
denom = (a1 * b2 - a2 * b1); | ||
if (!denom) return; // parallel lines havn't an intersection | ||
var cross = [ | ||
(b1 * c2 - b2 * c1) / denom, | ||
(a1 * c2 - a2 * c1) / -denom | ||
]; | ||
if ( | ||
!isNaN(cross[0]) && !isNaN(cross[1]) && | ||
isFinite(cross[0]) && isFinite(cross[1]) | ||
) { | ||
return cross; | ||
} | ||
var cross = [(b1 * c2 - b2 * c1) / denom, (a1 * c2 - a2 * c1) / -denom]; | ||
if ( | ||
!isNaN(cross[0]) && | ||
!isNaN(cross[1]) && | ||
isFinite(cross[0]) && | ||
isFinite(cross[1]) | ||
) { | ||
return cross; | ||
} | ||
} | ||
@@ -783,11 +754,12 @@ | ||
function strongRound(data) { | ||
for (var i = data.length; i-- > 0;) { | ||
if (data[i].toFixed(precision) != data[i]) { | ||
var rounded = +data[i].toFixed(precision - 1); | ||
data[i] = +Math.abs(rounded - data[i]).toFixed(precision + 1) >= error ? | ||
+data[i].toFixed(precision) : | ||
rounded; | ||
} | ||
for (var i = data.length; i-- > 0; ) { | ||
if (data[i].toFixed(precision) != data[i]) { | ||
var rounded = +data[i].toFixed(precision - 1); | ||
data[i] = | ||
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= error | ||
? +data[i].toFixed(precision) | ||
: rounded; | ||
} | ||
return data; | ||
} | ||
return data; | ||
} | ||
@@ -802,6 +774,6 @@ | ||
function round(data) { | ||
for (var i = data.length; i-- > 0;) { | ||
data[i] = Math.round(data[i]); | ||
} | ||
return data; | ||
for (var i = data.length; i-- > 0; ) { | ||
data[i] = Math.round(data[i]); | ||
} | ||
return data; | ||
} | ||
@@ -819,19 +791,17 @@ | ||
function isCurveStraightLine(data) { | ||
// Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points. | ||
var i = data.length - 2, | ||
a = -data[i + 1], // y1 − y2 (y1 = 0) | ||
b = data[i], // x2 − x1 (x1 = 0) | ||
d = 1 / (a * a + b * b); // same part for all points | ||
// Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points. | ||
var i = data.length - 2, | ||
a = -data[i + 1], // y1 − y2 (y1 = 0) | ||
b = data[i], // x2 − x1 (x1 = 0) | ||
d = 1 / (a * a + b * b); // same part for all points | ||
if (i <= 1 || !isFinite(d)) return false; // curve that ends at start point isn't the case | ||
if (i <= 1 || !isFinite(d)) return false; // curve that ends at start point isn't the case | ||
// Distance from point (x0, y0) to the line is sqrt((c − a·x0 − b·y0)² / (a² + b²)) | ||
while ((i -= 2) >= 0) { | ||
if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error) | ||
return false; | ||
} | ||
// Distance from point (x0, y0) to the line is sqrt((c − a·x0 − b·y0)² / (a² + b²)) | ||
while ((i -= 2) >= 0) { | ||
if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error) | ||
return false; | ||
} | ||
return true; | ||
return true; | ||
} | ||
@@ -847,8 +817,15 @@ | ||
function makeLonghand(item, data) { | ||
switch (item.instruction) { | ||
case 's': item.instruction = 'c'; break; | ||
case 't': item.instruction = 'q'; break; | ||
} | ||
item.data.unshift(data[data.length - 2] - data[data.length - 4], data[data.length - 1] - data[data.length - 3]); | ||
return item; | ||
switch (item.instruction) { | ||
case 's': | ||
item.instruction = 'c'; | ||
break; | ||
case 't': | ||
item.instruction = 'q'; | ||
break; | ||
} | ||
item.data.unshift( | ||
data[data.length - 2] - data[data.length - 4], | ||
data[data.length - 1] - data[data.length - 3] | ||
); | ||
return item; | ||
} | ||
@@ -865,3 +842,3 @@ | ||
function getDistance(point1, point2) { | ||
return Math.hypot(point1[0] - point2[0], point1[1] - point2[1]); | ||
return Math.hypot(point1[0] - point2[0], point1[1] - point2[1]); | ||
} | ||
@@ -880,11 +857,11 @@ | ||
function getCubicBezierPoint(curve, t) { | ||
var sqrT = t * t, | ||
cubT = sqrT * t, | ||
mt = 1 - t, | ||
sqrMt = mt * mt; | ||
var sqrT = t * t, | ||
cubT = sqrT * t, | ||
mt = 1 - t, | ||
sqrMt = mt * mt; | ||
return [ | ||
3 * sqrMt * t * curve[0] + 3 * mt * sqrT * curve[2] + cubT * curve[4], | ||
3 * sqrMt * t * curve[1] + 3 * mt * sqrT * curve[3] + cubT * curve[5] | ||
]; | ||
return [ | ||
3 * sqrMt * t * curve[0] + 3 * mt * sqrT * curve[2] + cubT * curve[4], | ||
3 * sqrMt * t * curve[1] + 3 * mt * sqrT * curve[3] + cubT * curve[5], | ||
]; | ||
} | ||
@@ -900,19 +877,30 @@ | ||
function findCircle(curve) { | ||
var midPoint = getCubicBezierPoint(curve, 1/2), | ||
m1 = [midPoint[0] / 2, midPoint[1] / 2], | ||
m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2], | ||
center = getIntersection([ | ||
m1[0], m1[1], | ||
m1[0] + m1[1], m1[1] - m1[0], | ||
m2[0], m2[1], | ||
m2[0] + (m2[1] - midPoint[1]), m2[1] - (m2[0] - midPoint[0]) | ||
]), | ||
radius = center && getDistance([0, 0], center), | ||
tolerance = Math.min(arcThreshold * error, arcTolerance * radius / 100); | ||
var midPoint = getCubicBezierPoint(curve, 1 / 2), | ||
m1 = [midPoint[0] / 2, midPoint[1] / 2], | ||
m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2], | ||
center = getIntersection([ | ||
m1[0], | ||
m1[1], | ||
m1[0] + m1[1], | ||
m1[1] - m1[0], | ||
m2[0], | ||
m2[1], | ||
m2[0] + (m2[1] - midPoint[1]), | ||
m2[1] - (m2[0] - midPoint[0]), | ||
]), | ||
radius = center && getDistance([0, 0], center), | ||
tolerance = Math.min(arcThreshold * error, (arcTolerance * radius) / 100); | ||
if (center && radius < 1e15 && | ||
[1/4, 3/4].every(function(point) { | ||
return Math.abs(getDistance(getCubicBezierPoint(curve, point), center) - radius) <= tolerance; | ||
})) | ||
return { center: center, radius: radius}; | ||
if ( | ||
center && | ||
radius < 1e15 && | ||
[1 / 4, 3 / 4].every(function (point) { | ||
return ( | ||
Math.abs( | ||
getDistance(getCubicBezierPoint(curve, point), center) - radius | ||
) <= tolerance | ||
); | ||
}) | ||
) | ||
return { center: center, radius: radius }; | ||
} | ||
@@ -929,7 +917,15 @@ | ||
function isArc(curve, circle) { | ||
var tolerance = Math.min(arcThreshold * error, arcTolerance * circle.radius / 100); | ||
var tolerance = Math.min( | ||
arcThreshold * error, | ||
(arcTolerance * circle.radius) / 100 | ||
); | ||
return [0, 1/4, 1/2, 3/4, 1].every(function(point) { | ||
return Math.abs(getDistance(getCubicBezierPoint(curve, point), circle.center) - circle.radius) <= tolerance; | ||
}); | ||
return [0, 1 / 4, 1 / 2, 3 / 4, 1].every(function (point) { | ||
return ( | ||
Math.abs( | ||
getDistance(getCubicBezierPoint(curve, point), circle.center) - | ||
circle.radius | ||
) <= tolerance | ||
); | ||
}); | ||
} | ||
@@ -946,6 +942,6 @@ | ||
function isArcPrev(curve, circle) { | ||
return isArc(curve, { | ||
center: [circle.center[0] + curve[4], circle.center[1] + curve[5]], | ||
radius: circle.radius | ||
}); | ||
return isArc(curve, { | ||
center: [circle.center[0] + curve[4], circle.center[1] + curve[5]], | ||
radius: circle.radius, | ||
}); | ||
} | ||
@@ -962,11 +958,10 @@ | ||
function findArcAngle(curve, relCircle) { | ||
var x1 = -relCircle.center[0], | ||
y1 = -relCircle.center[1], | ||
x2 = curve[4] - relCircle.center[0], | ||
y2 = curve[5] - relCircle.center[1]; | ||
var x1 = -relCircle.center[0], | ||
y1 = -relCircle.center[1], | ||
x2 = curve[4] - relCircle.center[0], | ||
y2 = curve[5] - relCircle.center[1]; | ||
return Math.acos( | ||
(x1 * x2 + y1 * y2) / | ||
Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)) | ||
); | ||
return Math.acos( | ||
(x1 * x2 + y1 * y2) / Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)) | ||
); | ||
} | ||
@@ -983,9 +978,9 @@ | ||
function data2Path(params, pathData) { | ||
return pathData.reduce(function(pathString, item) { | ||
var strData = ''; | ||
if (item.data) { | ||
strData = cleanupOutData(roundData(item.data.slice()), params); | ||
} | ||
return pathString + item.instruction + strData; | ||
}, ''); | ||
return pathData.reduce(function (pathString, item) { | ||
var strData = ''; | ||
if (item.data) { | ||
strData = cleanupOutData(roundData(item.data.slice()), params); | ||
} | ||
return pathString + item.instruction + strData; | ||
}, ''); | ||
} |
'use strict'; | ||
const { stringifyPathData } = require('../lib/path.js'); | ||
exports.type = 'perItem'; | ||
@@ -10,7 +12,8 @@ | ||
exports.params = { | ||
convertArcs: false | ||
convertArcs: false, | ||
floatPrecision: null, | ||
}; | ||
var none = { value: 0 }, | ||
regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; | ||
const none = { value: 0 }; | ||
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; | ||
@@ -30,122 +33,125 @@ /** | ||
*/ | ||
exports.fn = function(item, params) { | ||
var convertArcs = params && params.convertArcs; | ||
exports.fn = function (item, params) { | ||
const precision = params ? params.floatPrecision : null; | ||
const convertArcs = params && params.convertArcs; | ||
if ( | ||
item.isElem('rect') && | ||
item.hasAttr('width') && | ||
item.hasAttr('height') && | ||
!item.hasAttr('rx') && | ||
!item.hasAttr('ry') | ||
) { | ||
if ( | ||
item.isElem('rect') && | ||
item.hasAttr('width') && | ||
item.hasAttr('height') && | ||
!item.hasAttr('rx') && | ||
!item.hasAttr('ry') | ||
) { | ||
var x = +(item.attr('x') || none).value, | ||
y = +(item.attr('y') || none).value, | ||
width = +item.attr('width').value, | ||
height = +item.attr('height').value; | ||
// 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.addAttr({ | ||
name: 'd', | ||
value: stringifyPathData({ pathData, precision }), | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr(['x', 'y', 'width', 'height']); | ||
} | ||
var x = +(item.attr('x') || none).value, | ||
y = +(item.attr('y') || none).value, | ||
width = +item.attr('width').value, | ||
height = +item.attr('height').value; | ||
if (item.isElem('line')) { | ||
var x1 = +(item.attr('x1') || none).value, | ||
y1 = +(item.attr('y1') || none).value, | ||
x2 = +(item.attr('x2') || none).value, | ||
y2 = +(item.attr('y2') || none).value; | ||
if (isNaN(x1 - y1 + x2 - y2)) return; | ||
const pathData = [ | ||
{ command: 'M', args: [x1, y1] }, | ||
{ command: 'L', args: [x2, y2] }, | ||
]; | ||
item.addAttr({ | ||
name: 'd', | ||
value: stringifyPathData({ pathData, precision }), | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr(['x1', 'y1', 'x2', 'y2']); | ||
} | ||
// 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; | ||
if ( | ||
(item.isElem('polyline') || item.isElem('polygon')) && | ||
item.hasAttr('points') | ||
) { | ||
var coords = (item.attr('points').value.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.addAttr({ | ||
name: 'd', | ||
value: stringifyPathData({ pathData, precision }), | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr('points'); | ||
} | ||
var pathData = | ||
'M' + x + ' ' + y + | ||
'H' + (x + width) + | ||
'V' + (y + height) + | ||
'H' + x + | ||
'z'; | ||
if (item.isElem('circle') && convertArcs) { | ||
var cx = +(item.attr('cx') || none).value; | ||
var cy = +(item.attr('cy') || none).value; | ||
var r = +(item.attr('r') || none).value; | ||
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.addAttr({ | ||
name: 'd', | ||
value: stringifyPathData({ pathData, precision }), | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr(['cx', 'cy', 'r']); | ||
} | ||
item.addAttr({ | ||
name: 'd', | ||
value: pathData, | ||
prefix: '', | ||
local: 'd' | ||
}); | ||
item.renameElem('path') | ||
.removeAttr(['x', 'y', 'width', 'height']); | ||
} else if (item.isElem('line')) { | ||
var x1 = +(item.attr('x1') || none).value, | ||
y1 = +(item.attr('y1') || none).value, | ||
x2 = +(item.attr('x2') || none).value, | ||
y2 = +(item.attr('y2') || none).value; | ||
if (isNaN(x1 - y1 + x2 - y2)) return; | ||
item.addAttr({ | ||
name: 'd', | ||
value: 'M' + x1 + ' ' + y1 + 'L' + x2 + ' ' + y2, | ||
prefix: '', | ||
local: 'd' | ||
}); | ||
item.renameElem('path') | ||
.removeAttr(['x1', 'y1', 'x2', 'y2']); | ||
} else if (( | ||
item.isElem('polyline') || | ||
item.isElem('polygon') | ||
) && | ||
item.hasAttr('points') | ||
) { | ||
var coords = (item.attr('points').value.match(regNumber) || []).map(Number); | ||
if (coords.length < 4) return false; | ||
item.addAttr({ | ||
name: 'd', | ||
value: 'M' + coords.slice(0,2).join(' ') + | ||
'L' + coords.slice(2).join(' ') + | ||
(item.isElem('polygon') ? 'z' : ''), | ||
prefix: '', | ||
local: 'd' | ||
}); | ||
item.renameElem('path') | ||
.removeAttr('points'); | ||
} else if (item.isElem('circle') && convertArcs) { | ||
var cx = +(item.attr('cx') || none).value; | ||
var cy = +(item.attr('cy') || none).value; | ||
var r = +(item.attr('r') || none).value; | ||
if (isNaN(cx - cy + r)) { | ||
return; | ||
} | ||
var cPathData = | ||
'M' + cx + ' ' + (cy - r) + | ||
'A' + r + ' ' + r + ' 0 1 0 ' + cx + ' ' + (cy + r) + | ||
'A' + r + ' ' + r + ' 0 1 0 ' + cx + ' ' + (cy - r) + | ||
'Z'; | ||
item.addAttr({ | ||
name: 'd', | ||
value: cPathData, | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr(['cx', 'cy', 'r']); | ||
} else if (item.isElem('ellipse') && convertArcs) { | ||
var ecx = +(item.attr('cx') || none).value; | ||
var ecy = +(item.attr('cy') || none).value; | ||
var rx = +(item.attr('rx') || none).value; | ||
var ry = +(item.attr('ry') || none).value; | ||
if (isNaN(ecx - ecy + rx - ry)) { | ||
return; | ||
} | ||
var ePathData = | ||
'M' + ecx + ' ' + (ecy - ry) + | ||
'A' + rx + ' ' + ry + ' 0 1 0 ' + ecx + ' ' + (ecy + ry) + | ||
'A' + rx + ' ' + ry + ' 0 1 0 ' + ecx + ' ' + (ecy - ry) + | ||
'Z'; | ||
item.addAttr({ | ||
name: 'd', | ||
value: ePathData, | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr(['cx', 'cy', 'rx', 'ry']); | ||
if (item.isElem('ellipse') && convertArcs) { | ||
var ecx = +(item.attr('cx') || none).value; | ||
var ecy = +(item.attr('cy') || none).value; | ||
var rx = +(item.attr('rx') || none).value; | ||
var ry = +(item.attr('ry') || none).value; | ||
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.addAttr({ | ||
name: 'd', | ||
value: stringifyPathData({ pathData, precision }), | ||
prefix: '', | ||
local: 'd', | ||
}); | ||
item.renameElem('path').removeAttr(['cx', 'cy', 'rx', 'ry']); | ||
} | ||
}; |
@@ -8,6 +8,6 @@ 'use strict'; | ||
exports.params = { | ||
onlyMatchedOnce: true, | ||
removeMatchedSelectors: true, | ||
useMqs: ['', 'screen'], | ||
usePseudos: [''] | ||
onlyMatchedOnce: true, | ||
removeMatchedSelectors: true, | ||
useMqs: ['', 'screen'], | ||
usePseudos: [''], | ||
}; | ||
@@ -17,5 +17,4 @@ | ||
var csstree = require('css-tree'), | ||
cssTools = require('../lib/css-tools'); | ||
cssTools = require('../lib/css-tools'); | ||
@@ -46,200 +45,227 @@ /** | ||
*/ | ||
exports.fn = function(document, opts) { | ||
exports.fn = function (document, opts) { | ||
// collect <style/>s | ||
var styleEls = document.querySelectorAll('style'); | ||
// collect <style/>s | ||
var styleEls = document.querySelectorAll('style'); | ||
//no <styles/>s, nothing to do | ||
if (styleEls === null) { | ||
return document; | ||
} | ||
//no <styles/>s, nothing to do | ||
if (styleEls === null) { | ||
return document; | ||
var styles = [], | ||
selectors = []; | ||
for (var styleEl of styleEls) { | ||
// values other than the empty string or text/css are not used | ||
if ( | ||
styleEl.hasAttr('type') && | ||
styleEl.attr('type').value !== '' && | ||
styleEl.attr('type').value !== 'text/css' | ||
) { | ||
continue; | ||
} | ||
// skip empty <style/>s or <foreignObject> content. | ||
if (styleEl.isEmpty() || styleEl.closestElem('foreignObject')) { | ||
continue; | ||
} | ||
var styles = [], | ||
selectors = []; | ||
var cssStr = cssTools.getCssStr(styleEl); | ||
for (var styleEl of styleEls) { | ||
if (styleEl.isEmpty() || styleEl.closestElem('foreignObject')) { | ||
// skip empty <style/>s or <foreignObject> content. | ||
continue; | ||
} | ||
var cssStr = cssTools.getCssStr(styleEl); | ||
// collect <style/>s and their css ast | ||
var cssAst = {}; | ||
try { | ||
cssAst = csstree.parse(cssStr, { | ||
parseValue: false, | ||
parseCustomProperty: false | ||
}); | ||
} catch (parseError) { | ||
// console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError); | ||
continue; | ||
} | ||
styles.push({ | ||
styleEl: styleEl, | ||
cssAst: cssAst | ||
}); | ||
selectors = selectors.concat(cssTools.flattenToSelectors(cssAst)); | ||
// collect <style/>s and their css ast | ||
var cssAst = {}; | ||
try { | ||
cssAst = csstree.parse(cssStr, { | ||
parseValue: false, | ||
parseCustomProperty: false, | ||
}); | ||
} catch (parseError) { | ||
// console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError); | ||
continue; | ||
} | ||
styles.push({ | ||
styleEl: styleEl, | ||
cssAst: cssAst, | ||
}); | ||
// filter for mediaqueries to be used or without any mediaquery | ||
var selectorsMq = cssTools.filterByMqs(selectors, opts.useMqs); | ||
selectors = selectors.concat(cssTools.flattenToSelectors(cssAst)); | ||
} | ||
// filter for mediaqueries to be used or without any mediaquery | ||
var selectorsMq = cssTools.filterByMqs(selectors, opts.useMqs); | ||
// filter for pseudo elements to be used | ||
var selectorsPseudo = cssTools.filterByPseudos(selectorsMq, opts.usePseudos); | ||
// filter for pseudo elements to be used | ||
var selectorsPseudo = cssTools.filterByPseudos(selectorsMq, opts.usePseudos); | ||
// remove PseudoClass from its SimpleSelector for proper matching | ||
cssTools.cleanPseudos(selectorsPseudo); | ||
// remove PseudoClass from its SimpleSelector for proper matching | ||
cssTools.cleanPseudos(selectorsPseudo); | ||
// stable sort selectors | ||
var sortedSelectors = cssTools.sortSelectors(selectorsPseudo).reverse(); | ||
// stable sort selectors | ||
var sortedSelectors = cssTools.sortSelectors(selectorsPseudo).reverse(); | ||
var selector, selectedEl; | ||
// match selectors | ||
for (selector of sortedSelectors) { | ||
var selectorStr = csstree.generate(selector.item.data), | ||
selectedEls = null; | ||
var selector, | ||
selectedEl; | ||
try { | ||
selectedEls = document.querySelectorAll(selectorStr); | ||
} catch (selectError) { | ||
// console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError); | ||
continue; | ||
} | ||
// match selectors | ||
for (selector of sortedSelectors) { | ||
var selectorStr = csstree.generate(selector.item.data), | ||
selectedEls = null; | ||
if (selectedEls === null) { | ||
// nothing selected | ||
continue; | ||
} | ||
try { | ||
selectedEls = document.querySelectorAll(selectorStr); | ||
} catch (selectError) { | ||
// console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError); | ||
continue; | ||
} | ||
selector.selectedEls = selectedEls; | ||
} | ||
if (selectedEls === null) { | ||
// nothing selected | ||
continue; | ||
} | ||
// apply <style/> styles to matched elements | ||
for (selector of sortedSelectors) { | ||
if (!selector.selectedEls) { | ||
continue; | ||
} | ||
selector.selectedEls = selectedEls; | ||
if ( | ||
opts.onlyMatchedOnce && | ||
selector.selectedEls !== null && | ||
selector.selectedEls.length > 1 | ||
) { | ||
// skip selectors that match more than once if option onlyMatchedOnce is enabled | ||
continue; | ||
} | ||
// apply <style/> to matched elements | ||
for (selectedEl of selector.selectedEls) { | ||
if (selector.rule === null) { | ||
continue; | ||
} | ||
// apply <style/> styles to matched elements | ||
for (selector of sortedSelectors) { | ||
if(!selector.selectedEls) { | ||
continue; | ||
} | ||
// merge declarations | ||
csstree.walk(selector.rule, { | ||
visit: 'Declaration', | ||
enter: function (styleCsstreeDeclaration) { | ||
// existing inline styles have higher priority | ||
// no inline styles, external styles, external styles used | ||
// inline styles, external styles same priority as inline styles, inline styles used | ||
// inline styles, external styles higher priority than inline styles, external styles used | ||
var styleDeclaration = cssTools.csstreeToStyleDeclaration( | ||
styleCsstreeDeclaration | ||
); | ||
if ( | ||
selectedEl.style.getPropertyValue(styleDeclaration.name) !== null && | ||
selectedEl.style.getPropertyPriority(styleDeclaration.name) >= | ||
styleDeclaration.priority | ||
) { | ||
return; | ||
} | ||
selectedEl.style.setProperty( | ||
styleDeclaration.name, | ||
styleDeclaration.value, | ||
styleDeclaration.priority | ||
); | ||
}, | ||
}); | ||
} | ||
if (opts.onlyMatchedOnce && selector.selectedEls !== null && selector.selectedEls.length > 1) { | ||
// skip selectors that match more than once if option onlyMatchedOnce is enabled | ||
continue; | ||
} | ||
if ( | ||
opts.removeMatchedSelectors && | ||
selector.selectedEls !== null && | ||
selector.selectedEls.length > 0 | ||
) { | ||
// clean up matching simple selectors if option removeMatchedSelectors is enabled | ||
selector.rule.prelude.children.remove(selector.item); | ||
} | ||
} | ||
// apply <style/> to matched elements | ||
for (selectedEl of selector.selectedEls) { | ||
if (selector.rule === null) { | ||
continue; | ||
} | ||
if (!opts.removeMatchedSelectors) { | ||
return document; // no further processing required | ||
} | ||
// merge declarations | ||
csstree.walk(selector.rule, {visit: 'Declaration', enter: function(styleCsstreeDeclaration) { | ||
// clean up matched class + ID attribute values | ||
for (selector of sortedSelectors) { | ||
if (!selector.selectedEls) { | ||
continue; | ||
} | ||
// existing inline styles have higher priority | ||
// no inline styles, external styles, external styles used | ||
// inline styles, external styles same priority as inline styles, inline styles used | ||
// inline styles, external styles higher priority than inline styles, external styles used | ||
var styleDeclaration = cssTools.csstreeToStyleDeclaration(styleCsstreeDeclaration); | ||
if (selectedEl.style.getPropertyValue(styleDeclaration.name) !== null && | ||
selectedEl.style.getPropertyPriority(styleDeclaration.name) >= styleDeclaration.priority) { | ||
return; | ||
} | ||
selectedEl.style.setProperty(styleDeclaration.name, styleDeclaration.value, styleDeclaration.priority); | ||
}}); | ||
} | ||
if (opts.removeMatchedSelectors && selector.selectedEls !== null && selector.selectedEls.length > 0) { | ||
// clean up matching simple selectors if option removeMatchedSelectors is enabled | ||
selector.rule.prelude.children.remove(selector.item); | ||
} | ||
if ( | ||
opts.onlyMatchedOnce && | ||
selector.selectedEls !== null && | ||
selector.selectedEls.length > 1 | ||
) { | ||
// skip selectors that match more than once if option onlyMatchedOnce is enabled | ||
continue; | ||
} | ||
for (selectedEl of selector.selectedEls) { | ||
// class | ||
var firstSubSelector = selector.item.data.children.first(); | ||
if (firstSubSelector.type === 'ClassSelector') { | ||
selectedEl.class.remove(firstSubSelector.name); | ||
} | ||
// clean up now empty class attributes | ||
if (typeof selectedEl.class.item(0) === 'undefined') { | ||
selectedEl.removeAttr('class'); | ||
} | ||
if (!opts.removeMatchedSelectors) { | ||
return document; // no further processing required | ||
// ID | ||
if (firstSubSelector.type === 'IdSelector') { | ||
selectedEl.removeAttr('id', firstSubSelector.name); | ||
} | ||
} | ||
} | ||
// clean up matched class + ID attribute values | ||
for (selector of sortedSelectors) { | ||
if(!selector.selectedEls) { | ||
continue; | ||
// clean up now empty elements | ||
for (var style of styles) { | ||
csstree.walk(style.cssAst, { | ||
visit: 'Rule', | ||
enter: function (node, item, list) { | ||
// clean up <style/> atrules without any rulesets left | ||
if ( | ||
node.type === 'Atrule' && | ||
// only Atrules containing rulesets | ||
node.block !== null && | ||
node.block.children.isEmpty() | ||
) { | ||
list.remove(item); | ||
return; | ||
} | ||
if (opts.onlyMatchedOnce && selector.selectedEls !== null && selector.selectedEls.length > 1) { | ||
// skip selectors that match more than once if option onlyMatchedOnce is enabled | ||
continue; | ||
// clean up <style/> rulesets without any css selectors left | ||
if (node.type === 'Rule' && node.prelude.children.isEmpty()) { | ||
list.remove(item); | ||
} | ||
}, | ||
}); | ||
for (selectedEl of selector.selectedEls) { | ||
// class | ||
var firstSubSelector = selector.item.data.children.first(); | ||
if(firstSubSelector.type === 'ClassSelector') { | ||
selectedEl.class.remove(firstSubSelector.name); | ||
} | ||
// clean up now empty class attributes | ||
if(typeof selectedEl.class.item(0) === 'undefined') { | ||
selectedEl.removeAttr('class'); | ||
} | ||
if (style.cssAst.children.isEmpty()) { | ||
// clean up now emtpy <style/>s | ||
var styleParentEl = style.styleEl.parentNode; | ||
styleParentEl.spliceContent( | ||
styleParentEl.content.indexOf(style.styleEl), | ||
1 | ||
); | ||
// ID | ||
if(firstSubSelector.type === 'IdSelector') { | ||
selectedEl.removeAttr('id', firstSubSelector.name); | ||
} | ||
} | ||
} | ||
if (styleParentEl.elem === 'defs' && styleParentEl.content.length === 0) { | ||
// also clean up now empty <def/>s | ||
var defsParentEl = styleParentEl.parentNode; | ||
defsParentEl.spliceContent( | ||
defsParentEl.content.indexOf(styleParentEl), | ||
1 | ||
); | ||
} | ||
// clean up now empty elements | ||
for (var style of styles) { | ||
csstree.walk(style.cssAst, {visit: 'Rule', enter: function(node, item, list) { | ||
// clean up <style/> atrules without any rulesets left | ||
if (node.type === 'Atrule' && | ||
// only Atrules containing rulesets | ||
node.block !== null && | ||
node.block.children.isEmpty()) { | ||
list.remove(item); | ||
return; | ||
} | ||
// clean up <style/> rulesets without any css selectors left | ||
if (node.type === 'Rule' && | ||
node.prelude.children.isEmpty()) { | ||
list.remove(item); | ||
} | ||
}}); | ||
if (style.cssAst.children.isEmpty()) { | ||
// clean up now emtpy <style/>s | ||
var styleParentEl = style.styleEl.parentNode; | ||
styleParentEl.spliceContent(styleParentEl.content.indexOf(style.styleEl), 1); | ||
if (styleParentEl.elem === 'defs' && | ||
styleParentEl.content.length === 0) { | ||
// also clean up now empty <def/>s | ||
var defsParentEl = styleParentEl.parentNode; | ||
defsParentEl.spliceContent(defsParentEl.content.indexOf(styleParentEl), 1); | ||
} | ||
continue; | ||
} | ||
// update existing, left over <style>s | ||
cssTools.setCssStr(style.styleEl, csstree.generate(style.cssAst)); | ||
continue; | ||
} | ||
// update existing, left over <style>s | ||
cssTools.setCssStr(style.styleEl, csstree.generate(style.cssAst)); | ||
} | ||
return document; | ||
return document; | ||
}; |
'use strict'; | ||
const { computeStyle } = require('../lib/style.js'); | ||
const { path2js, js2path, intersects } = require('./_path.js'); | ||
exports.type = 'perItem'; | ||
@@ -10,13 +13,9 @@ | ||
exports.params = { | ||
collapseRepeated: true, | ||
force: false, | ||
leadingZero: true, | ||
negativeExtraSpace: true, | ||
noSpaceAfterFlags: false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
collapseRepeated: true, | ||
force: false, | ||
leadingZero: true, | ||
negativeExtraSpace: true, | ||
noSpaceAfterFlags: false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
}; | ||
var path2js = require('./_path.js').path2js, | ||
js2path = require('./_path.js').js2path, | ||
intersects = require('./_path.js').intersects; | ||
/** | ||
@@ -30,46 +29,54 @@ * Merge multiple Paths into one. | ||
*/ | ||
exports.fn = function(item, params) { | ||
exports.fn = function (item, params) { | ||
if (!item.isElem() || item.isEmpty()) return; | ||
if (!item.isElem() || item.isEmpty()) return; | ||
var prevContentItem = null, | ||
prevContentItemKeys = null; | ||
var prevContentItem = null, | ||
prevContentItemKeys = null; | ||
item.content = item.content.filter(function (contentItem) { | ||
if ( | ||
prevContentItem && | ||
prevContentItem.isElem('path') && | ||
prevContentItem.isEmpty() && | ||
prevContentItem.hasAttr('d') && | ||
contentItem.isElem('path') && | ||
contentItem.isEmpty() && | ||
contentItem.hasAttr('d') | ||
) { | ||
const computedStyle = computeStyle(contentItem); | ||
// keep path to not break markers | ||
if ( | ||
computedStyle['marker-start'] || | ||
computedStyle['marker-mid'] || | ||
computedStyle['marker-end'] | ||
) { | ||
return true; | ||
} | ||
if (!prevContentItemKeys) { | ||
prevContentItemKeys = Object.keys(prevContentItem.attrs); | ||
} | ||
item.content = item.content.filter(function(contentItem) { | ||
var contentItemAttrs = Object.keys(contentItem.attrs), | ||
equalData = | ||
prevContentItemKeys.length == contentItemAttrs.length && | ||
contentItemAttrs.every(function (key) { | ||
return ( | ||
key == 'd' || | ||
(prevContentItem.hasAttr(key) && | ||
prevContentItem.attr(key).value == contentItem.attr(key).value) | ||
); | ||
}), | ||
prevPathJS = path2js(prevContentItem), | ||
curPathJS = path2js(contentItem); | ||
if (prevContentItem && | ||
prevContentItem.isElem('path') && | ||
prevContentItem.isEmpty() && | ||
prevContentItem.hasAttr('d') && | ||
contentItem.isElem('path') && | ||
contentItem.isEmpty() && | ||
contentItem.hasAttr('d') | ||
) { | ||
if (equalData && (params.force || !intersects(prevPathJS, curPathJS))) { | ||
js2path(prevContentItem, prevPathJS.concat(curPathJS), params); | ||
return false; | ||
} | ||
} | ||
if (!prevContentItemKeys) { | ||
prevContentItemKeys = Object.keys(prevContentItem.attrs); | ||
} | ||
var contentItemAttrs = Object.keys(contentItem.attrs), | ||
equalData = prevContentItemKeys.length == contentItemAttrs.length && | ||
contentItemAttrs.every(function(key) { | ||
return key == 'd' || | ||
prevContentItem.hasAttr(key) && | ||
prevContentItem.attr(key).value == contentItem.attr(key).value; | ||
}), | ||
prevPathJS = path2js(prevContentItem), | ||
curPathJS = path2js(contentItem); | ||
if (equalData && (params.force || !intersects(prevPathJS, curPathJS))) { | ||
js2path(prevContentItem, prevPathJS.concat(curPathJS), params); | ||
return false; | ||
} | ||
} | ||
prevContentItem = contentItem; | ||
prevContentItemKeys = null; | ||
return true; | ||
}); | ||
prevContentItem = contentItem; | ||
prevContentItemKeys = null; | ||
return true; | ||
}); | ||
}; |
'use strict'; | ||
const { attrsGroups } = require('./_collections.js'); | ||
exports.type = 'perItem'; | ||
@@ -17,14 +19,14 @@ | ||
*/ | ||
exports.fn = function(item) { | ||
if (item.elem) { | ||
item.eachAttr(function(attr) { | ||
if (attr.value === '') { | ||
item.removeAttr(attr.name); | ||
} | ||
}); | ||
} | ||
exports.fn = function (item) { | ||
if (item.elem) { | ||
item.eachAttr(function (attr) { | ||
if ( | ||
attr.value === '' && | ||
// empty conditional processing attributes prevents elements from rendering | ||
attrsGroups.conditionalProcessing.includes(attr.name) === false | ||
) { | ||
item.removeAttr(attr.name); | ||
} | ||
}); | ||
} | ||
}; |
@@ -27,3 +27,3 @@ 'use strict'; | ||
*/ | ||
exports.fn = function(item) { | ||
exports.fn = function (item) { | ||
return ( | ||
@@ -33,3 +33,4 @@ item.isElem(container) === false || | ||
item.isElem('svg') || | ||
(item.isElem('pattern') && item.hasAttrLocal('href')) || | ||
// empty patterns may contain reusable configuration | ||
(item.isElem('pattern') && Object.keys(item.attrs).length !== 0) || | ||
// The 'g' may not have content, but the filter may cause a rectangle | ||
@@ -36,0 +37,0 @@ // to be created and filled with pattern. |
'use strict'; | ||
const { computeStyle } = require('../lib/style.js'); | ||
const { parsePathData } = require('../lib/path.js'); | ||
exports.type = 'perItem'; | ||
@@ -7,24 +10,23 @@ | ||
exports.description = 'removes hidden elements (zero sized, with absent attributes)'; | ||
exports.description = | ||
'removes hidden elements (zero sized, with absent attributes)'; | ||
exports.params = { | ||
isHidden: true, | ||
displayNone: true, | ||
opacity0: true, | ||
circleR0: true, | ||
ellipseRX0: true, | ||
ellipseRY0: true, | ||
rectWidth0: true, | ||
rectHeight0: true, | ||
patternWidth0: true, | ||
patternHeight0: true, | ||
imageWidth0: true, | ||
imageHeight0: true, | ||
pathEmptyD: true, | ||
polylineEmptyPoints: true, | ||
polygonEmptyPoints: true | ||
isHidden: true, | ||
displayNone: true, | ||
opacity0: true, | ||
circleR0: true, | ||
ellipseRX0: true, | ||
ellipseRY0: true, | ||
rectWidth0: true, | ||
rectHeight0: true, | ||
patternWidth0: true, | ||
patternHeight0: true, | ||
imageWidth0: true, | ||
imageHeight0: true, | ||
pathEmptyD: true, | ||
polylineEmptyPoints: true, | ||
polygonEmptyPoints: true, | ||
}; | ||
var regValidPath = /M\s*(?:[-+]?(?:\d*\.\d+|\d+(?:\.|(?!\.)))([eE][-+]?\d+)?(?!\d)\s*,?\s*){2}\D*\d/i; | ||
/** | ||
@@ -50,182 +52,229 @@ * Remove hidden elements with disabled rendering: | ||
exports.fn = function (item, params) { | ||
if (item.elem) { | ||
// Removes hidden elements | ||
// https://www.w3schools.com/cssref/pr_class_visibility.asp | ||
const computedStyle = computeStyle(item); | ||
if ( | ||
params.isHidden && | ||
computedStyle.visibility && | ||
computedStyle.visibility.type === 'static' && | ||
computedStyle.visibility.value === 'hidden' && | ||
// keep if any descendant enables visibility | ||
item.querySelector('[visibility=visible]') == null | ||
) { | ||
return false; | ||
} | ||
if (item.elem) { | ||
// Removes hidden elements | ||
// https://www.w3schools.com/cssref/pr_class_visibility.asp | ||
if ( | ||
params.isHidden && | ||
item.hasAttr('visibility', 'hidden') && | ||
// keep if any descendant enables visibility | ||
item.querySelector('[visibility=visible]') == null | ||
) return false; | ||
// display="none" | ||
// | ||
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty | ||
// "A value of display: none indicates that the given element | ||
// and its children shall not be rendered directly" | ||
if ( | ||
params.displayNone && | ||
computedStyle.display && | ||
computedStyle.display.type === 'static' && | ||
computedStyle.display.value === 'none' && | ||
// markers with display: none still rendered | ||
item.isElem('marker') === false | ||
) { | ||
return false; | ||
} | ||
// display="none" | ||
// | ||
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty | ||
// "A value of display: none indicates that the given element | ||
// and its children shall not be rendered directly" | ||
if ( | ||
params.displayNone && | ||
item.hasAttr('display', 'none') | ||
) return false; | ||
// opacity="0" | ||
// | ||
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties | ||
if ( | ||
params.opacity0 && | ||
computedStyle.opacity && | ||
computedStyle.opacity.type === 'static' && | ||
computedStyle.opacity.value === '0' && | ||
// transparent element inside clipPath still affect clipped elements | ||
item.closestElem('clipPath') == null | ||
) { | ||
return false; | ||
} | ||
// opacity="0" | ||
// | ||
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties | ||
if ( | ||
params.opacity0 && | ||
item.hasAttr('opacity', '0') && | ||
// transparent element inside clipPath still affect clipped elements | ||
item.closestElem('clipPath') == null | ||
) return false; | ||
// Circles with zero radius | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <circle r="0"> | ||
if ( | ||
params.circleR0 && | ||
item.isElem('circle') && | ||
item.isEmpty() && | ||
item.hasAttr('r', '0') | ||
) { | ||
return false; | ||
} | ||
// Circles with zero radius | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <circle r="0"> | ||
if ( | ||
params.circleR0 && | ||
item.isElem('circle') && | ||
item.isEmpty() && | ||
item.hasAttr('r', '0') | ||
) return false; | ||
// Ellipse with zero x-axis radius | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <ellipse rx="0"> | ||
if ( | ||
params.ellipseRX0 && | ||
item.isElem('ellipse') && | ||
item.isEmpty() && | ||
item.hasAttr('rx', '0') | ||
) { | ||
return false; | ||
} | ||
// Ellipse with zero x-axis radius | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <ellipse rx="0"> | ||
if ( | ||
params.ellipseRX0 && | ||
item.isElem('ellipse') && | ||
item.isEmpty() && | ||
item.hasAttr('rx', '0') | ||
) return false; | ||
// Ellipse with zero y-axis radius | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <ellipse ry="0"> | ||
if ( | ||
params.ellipseRY0 && | ||
item.isElem('ellipse') && | ||
item.isEmpty() && | ||
item.hasAttr('ry', '0') | ||
) { | ||
return false; | ||
} | ||
// Ellipse with zero y-axis radius | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <ellipse ry="0"> | ||
if ( | ||
params.ellipseRY0 && | ||
item.isElem('ellipse') && | ||
item.isEmpty() && | ||
item.hasAttr('ry', '0') | ||
) return false; | ||
// Rectangle with zero width | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <rect width="0"> | ||
if ( | ||
params.rectWidth0 && | ||
item.isElem('rect') && | ||
item.isEmpty() && | ||
item.hasAttr('width', '0') | ||
) { | ||
return false; | ||
} | ||
// Rectangle with zero width | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <rect width="0"> | ||
if ( | ||
params.rectWidth0 && | ||
item.isElem('rect') && | ||
item.isEmpty() && | ||
item.hasAttr('width', '0') | ||
) return false; | ||
// Rectangle with zero height | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <rect height="0"> | ||
if ( | ||
params.rectHeight0 && | ||
params.rectWidth0 && | ||
item.isElem('rect') && | ||
item.isEmpty() && | ||
item.hasAttr('height', '0') | ||
) { | ||
return false; | ||
} | ||
// Rectangle with zero height | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <rect height="0"> | ||
if ( | ||
params.rectHeight0 && | ||
params.rectWidth0 && | ||
item.isElem('rect') && | ||
item.isEmpty() && | ||
item.hasAttr('height', '0') | ||
) return false; | ||
// Pattern with zero width | ||
// | ||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute | ||
// "A value of zero disables rendering of the element (i.e., no paint is applied)" | ||
// | ||
// <pattern width="0"> | ||
if ( | ||
params.patternWidth0 && | ||
item.isElem('pattern') && | ||
item.hasAttr('width', '0') | ||
) { | ||
return false; | ||
} | ||
// Pattern with zero width | ||
// | ||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute | ||
// "A value of zero disables rendering of the element (i.e., no paint is applied)" | ||
// | ||
// <pattern width="0"> | ||
if ( | ||
params.patternWidth0 && | ||
item.isElem('pattern') && | ||
item.hasAttr('width', '0') | ||
) return false; | ||
// Pattern with zero height | ||
// | ||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute | ||
// "A value of zero disables rendering of the element (i.e., no paint is applied)" | ||
// | ||
// <pattern height="0"> | ||
if ( | ||
params.patternHeight0 && | ||
item.isElem('pattern') && | ||
item.hasAttr('height', '0') | ||
) { | ||
return false; | ||
} | ||
// Pattern with zero height | ||
// | ||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute | ||
// "A value of zero disables rendering of the element (i.e., no paint is applied)" | ||
// | ||
// <pattern height="0"> | ||
if ( | ||
params.patternHeight0 && | ||
item.isElem('pattern') && | ||
item.hasAttr('height', '0') | ||
) return false; | ||
// Image with zero width | ||
// | ||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <image width="0"> | ||
if ( | ||
params.imageWidth0 && | ||
item.isElem('image') && | ||
item.hasAttr('width', '0') | ||
) { | ||
return false; | ||
} | ||
// Image with zero width | ||
// | ||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <image width="0"> | ||
if ( | ||
params.imageWidth0 && | ||
item.isElem('image') && | ||
item.hasAttr('width', '0') | ||
) return false; | ||
// Image with zero height | ||
// | ||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <image height="0"> | ||
if ( | ||
params.imageHeight0 && | ||
item.isElem('image') && | ||
item.hasAttr('height', '0') | ||
) { | ||
return false; | ||
} | ||
// Image with zero height | ||
// | ||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute | ||
// "A value of zero disables rendering of the element" | ||
// | ||
// <image height="0"> | ||
if ( | ||
params.imageHeight0 && | ||
item.isElem('image') && | ||
item.hasAttr('height', '0') | ||
) return false; | ||
// Path with empty data | ||
// | ||
// https://www.w3.org/TR/SVG11/paths.html#DAttribute | ||
// | ||
// <path d=""/> | ||
if (params.pathEmptyD && item.isElem('path')) { | ||
if (item.hasAttr('d') === false) { | ||
return false; | ||
} | ||
const pathData = parsePathData(item.attr('d').value); | ||
if (pathData.length === 0) { | ||
return false; | ||
} | ||
// keep single point paths for markers | ||
if ( | ||
pathData.length === 1 && | ||
computedStyle['marker-start'] == null && | ||
computedStyle['marker-end'] == null | ||
) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
// Path with empty data | ||
// | ||
// https://www.w3.org/TR/SVG11/paths.html#DAttribute | ||
// | ||
// <path d=""/> | ||
if ( | ||
params.pathEmptyD && | ||
item.isElem('path') && | ||
(!item.hasAttr('d') || !regValidPath.test(item.attr('d').value)) | ||
) return false; | ||
// Polyline with empty points | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute | ||
// | ||
// <polyline points=""> | ||
if ( | ||
params.polylineEmptyPoints && | ||
item.isElem('polyline') && | ||
!item.hasAttr('points') | ||
) { | ||
return false; | ||
} | ||
// Polyline with empty points | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute | ||
// | ||
// <polyline points=""> | ||
if ( | ||
params.polylineEmptyPoints && | ||
item.isElem('polyline') && | ||
!item.hasAttr('points') | ||
) return false; | ||
// Polygon with empty points | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute | ||
// | ||
// <polygon points=""> | ||
if ( | ||
params.polygonEmptyPoints && | ||
item.isElem('polygon') && | ||
!item.hasAttr('points') | ||
) return false; | ||
// Polygon with empty points | ||
// | ||
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute | ||
// | ||
// <polygon points=""> | ||
if ( | ||
params.polygonEmptyPoints && | ||
item.isElem('polygon') && | ||
!item.hasAttr('points') | ||
) { | ||
return false; | ||
} | ||
} | ||
}; |
@@ -26,24 +26,25 @@ 'use strict'; | ||
*/ | ||
exports.fn = function(item) { | ||
exports.fn = function (item) { | ||
if ( | ||
item.isElem(viewBoxElems) && | ||
item.hasAttr('viewBox') && | ||
item.hasAttr('width') && | ||
item.hasAttr('height') | ||
) { | ||
// TODO remove width/height for such case instead | ||
if (item.isElem('svg') && item.closestElem('svg')) { | ||
return; | ||
} | ||
var nums = item.attr('viewBox').value.split(/[ ,]+/g); | ||
if ( | ||
item.isElem(viewBoxElems) && | ||
item.hasAttr('viewBox') && | ||
item.hasAttr('width') && | ||
item.hasAttr('height') | ||
nums[0] === '0' && | ||
nums[1] === '0' && | ||
item.attr('width').value.replace(/px$/, '') === nums[2] && // could use parseFloat too | ||
item.attr('height').value.replace(/px$/, '') === nums[3] | ||
) { | ||
var nums = item.attr('viewBox').value.split(/[ ,]+/g); | ||
if ( | ||
nums[0] === '0' && | ||
nums[1] === '0' && | ||
item.attr('width').value.replace(/px$/, '') === nums[2] && // could use parseFloat too | ||
item.attr('height').value.replace(/px$/, '') === nums[3] | ||
) { | ||
item.removeAttr('viewBox'); | ||
} | ||
item.removeAttr('viewBox'); | ||
} | ||
} | ||
}; |
@@ -29,3 +29,3 @@ <div align="center"> | ||
```sh | ||
svgo one.svg two.svg -p one.min.svg two.min.svg | ||
svgo one.svg two.svg -o one.min.svg two.min.svg | ||
``` | ||
@@ -32,0 +32,0 @@ |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7
75
34466
1158220
19
- Removedcss-select-base-adapter@^0.1.1
- Removedcss-select-base-adapter@0.1.1(transitive)