summaryrefslogtreecommitdiff
path: root/node_modules/svgo/plugins/removeOffCanvasPaths.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/svgo/plugins/removeOffCanvasPaths.js')
-rw-r--r--node_modules/svgo/plugins/removeOffCanvasPaths.js136
1 files changed, 136 insertions, 0 deletions
diff --git a/node_modules/svgo/plugins/removeOffCanvasPaths.js b/node_modules/svgo/plugins/removeOffCanvasPaths.js
new file mode 100644
index 00000000..7a3d131d
--- /dev/null
+++ b/node_modules/svgo/plugins/removeOffCanvasPaths.js
@@ -0,0 +1,136 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').PathDataItem} PathDataItem
+ */
+
+const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
+const { parsePathData } = require('../lib/path.js');
+const { intersects } = require('./_path.js');
+
+exports.name = 'removeOffCanvasPaths';
+exports.description =
+ 'removes elements that are drawn outside of the viewbox (disabled by default)';
+
+/**
+ * Remove elements that are drawn outside of the viewbox.
+ *
+ * @author JoshyPHP
+ *
+ * @type {import('./plugins-types').Plugin<'removeOffCanvasPaths'>}
+ */
+exports.fn = () => {
+ /**
+ * @type {null | {
+ * top: number,
+ * right: number,
+ * bottom: number,
+ * left: number,
+ * width: number,
+ * height: number
+ * }}
+ */
+ let viewBoxData = null;
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ let viewBox = '';
+ // find viewbox
+ if (node.attributes.viewBox != null) {
+ // remove commas and plus signs, normalize and trim whitespace
+ viewBox = node.attributes.viewBox;
+ } else if (
+ node.attributes.height != null &&
+ node.attributes.width != null
+ ) {
+ viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
+ }
+
+ // parse viewbox
+ // remove commas and plus signs, normalize and trim whitespace
+ viewBox = viewBox
+ .replace(/[,+]|px/g, ' ')
+ .replace(/\s+/g, ' ')
+ .replace(/^\s*|\s*$/g, '');
+ // ensure that the dimensions are 4 values separated by space
+ const m =
+ /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
+ viewBox
+ );
+ if (m == null) {
+ return;
+ }
+ const left = Number.parseFloat(m[1]);
+ const top = Number.parseFloat(m[2]);
+ const width = Number.parseFloat(m[3]);
+ const height = Number.parseFloat(m[4]);
+
+ // store the viewBox boundaries
+ viewBoxData = {
+ left,
+ top,
+ right: left + width,
+ bottom: top + height,
+ width,
+ height,
+ };
+ }
+
+ // consider that any item with a transform attribute is visible
+ if (node.attributes.transform != null) {
+ return visitSkip;
+ }
+
+ if (
+ node.name === 'path' &&
+ node.attributes.d != null &&
+ viewBoxData != null
+ ) {
+ const pathData = parsePathData(node.attributes.d);
+
+ // consider that a M command within the viewBox is visible
+ let visible = false;
+ for (const pathDataItem of pathData) {
+ if (pathDataItem.command === 'M') {
+ const [x, y] = pathDataItem.args;
+ if (
+ x >= viewBoxData.left &&
+ x <= viewBoxData.right &&
+ y >= viewBoxData.top &&
+ y <= viewBoxData.bottom
+ ) {
+ visible = true;
+ }
+ }
+ }
+ if (visible) {
+ return;
+ }
+
+ if (pathData.length === 2) {
+ // close the path too short for intersects()
+ pathData.push({ command: 'z', args: [] });
+ }
+
+ const { left, top, width, height } = viewBoxData;
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const viewBoxPathData = [
+ { command: 'M', args: [left, top] },
+ { command: 'h', args: [width] },
+ { command: 'v', args: [height] },
+ { command: 'H', args: [left] },
+ { command: 'z', args: [] },
+ ];
+
+ if (intersects(viewBoxPathData, pathData) === false) {
+ detachNodeFromParent(node, parentNode);
+ }
+ }
+ },
+ },
+ };
+};