99 lines
2.8 KiB
JavaScript
99 lines
2.8 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const { detachNodeFromParent } = require('../lib/xast.js');
|
||
|
const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
||
|
const { path2js, js2path, intersects } = require('./_path.js');
|
||
|
|
||
|
exports.name = 'mergePaths';
|
||
|
exports.description = 'merges multiple paths in one if possible';
|
||
|
|
||
|
/**
|
||
|
* Merge multiple Paths into one.
|
||
|
*
|
||
|
* @author Kir Belevich, Lev Solntsev
|
||
|
*
|
||
|
* @type {import('./plugins-types').Plugin<'mergePaths'>}
|
||
|
*/
|
||
|
exports.fn = (root, params) => {
|
||
|
const {
|
||
|
force = false,
|
||
|
floatPrecision,
|
||
|
noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||
|
} = params;
|
||
|
const stylesheet = collectStylesheet(root);
|
||
|
|
||
|
return {
|
||
|
element: {
|
||
|
enter: (node) => {
|
||
|
let prevChild = null;
|
||
|
|
||
|
for (const child of node.children) {
|
||
|
// skip if previous element is not path or contains animation elements
|
||
|
if (
|
||
|
prevChild == null ||
|
||
|
prevChild.type !== 'element' ||
|
||
|
prevChild.name !== 'path' ||
|
||
|
prevChild.children.length !== 0 ||
|
||
|
prevChild.attributes.d == null
|
||
|
) {
|
||
|
prevChild = child;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// skip if element is not path or contains animation elements
|
||
|
if (
|
||
|
child.type !== 'element' ||
|
||
|
child.name !== 'path' ||
|
||
|
child.children.length !== 0 ||
|
||
|
child.attributes.d == null
|
||
|
) {
|
||
|
prevChild = child;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// preserve paths with markers
|
||
|
const computedStyle = computeStyle(stylesheet, child);
|
||
|
if (
|
||
|
computedStyle['marker-start'] ||
|
||
|
computedStyle['marker-mid'] ||
|
||
|
computedStyle['marker-end']
|
||
|
) {
|
||
|
prevChild = child;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const prevChildAttrs = Object.keys(prevChild.attributes);
|
||
|
const childAttrs = Object.keys(child.attributes);
|
||
|
let attributesAreEqual = prevChildAttrs.length === childAttrs.length;
|
||
|
for (const name of childAttrs) {
|
||
|
if (name !== 'd') {
|
||
|
if (
|
||
|
prevChild.attributes[name] == null ||
|
||
|
prevChild.attributes[name] !== child.attributes[name]
|
||
|
) {
|
||
|
attributesAreEqual = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
const prevPathJS = path2js(prevChild);
|
||
|
const curPathJS = path2js(child);
|
||
|
|
||
|
if (
|
||
|
attributesAreEqual &&
|
||
|
(force || !intersects(prevPathJS, curPathJS))
|
||
|
) {
|
||
|
js2path(prevChild, prevPathJS.concat(curPathJS), {
|
||
|
floatPrecision,
|
||
|
noSpaceAfterFlags,
|
||
|
});
|
||
|
detachNodeFromParent(child, node);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
prevChild = child;
|
||
|
}
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
};
|