1 | 'use strict';
|
---|
2 |
|
---|
3 | /**
|
---|
4 | * @typedef {import('../lib/types').PathDataItem} PathDataItem
|
---|
5 | */
|
---|
6 |
|
---|
7 | const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
---|
8 | const { parsePathData } = require('../lib/path.js');
|
---|
9 | const { intersects } = require('./_path.js');
|
---|
10 |
|
---|
11 | exports.type = 'visitor';
|
---|
12 | exports.name = 'removeOffCanvasPaths';
|
---|
13 | exports.active = false;
|
---|
14 | exports.description =
|
---|
15 | 'removes elements that are drawn outside of the viewbox (disabled by default)';
|
---|
16 |
|
---|
17 | /**
|
---|
18 | * Remove elements that are drawn outside of the viewbox.
|
---|
19 | *
|
---|
20 | * @author JoshyPHP
|
---|
21 | *
|
---|
22 | * @type {import('../lib/types').Plugin<void>}
|
---|
23 | */
|
---|
24 | exports.fn = () => {
|
---|
25 | /**
|
---|
26 | * @type {null | {
|
---|
27 | * top: number,
|
---|
28 | * right: number,
|
---|
29 | * bottom: number,
|
---|
30 | * left: number,
|
---|
31 | * width: number,
|
---|
32 | * height: number
|
---|
33 | * }}
|
---|
34 | */
|
---|
35 | let viewBoxData = null;
|
---|
36 |
|
---|
37 | return {
|
---|
38 | element: {
|
---|
39 | enter: (node, parentNode) => {
|
---|
40 | if (node.name === 'svg' && parentNode.type === 'root') {
|
---|
41 | let viewBox = '';
|
---|
42 | // find viewbox
|
---|
43 | if (node.attributes.viewBox != null) {
|
---|
44 | // remove commas and plus signs, normalize and trim whitespace
|
---|
45 | viewBox = node.attributes.viewBox;
|
---|
46 | } else if (
|
---|
47 | node.attributes.height != null &&
|
---|
48 | node.attributes.width != null
|
---|
49 | ) {
|
---|
50 | viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
|
---|
51 | }
|
---|
52 |
|
---|
53 | // parse viewbox
|
---|
54 | // remove commas and plus signs, normalize and trim whitespace
|
---|
55 | viewBox = viewBox
|
---|
56 | .replace(/[,+]|px/g, ' ')
|
---|
57 | .replace(/\s+/g, ' ')
|
---|
58 | .replace(/^\s*|\s*$/g, '');
|
---|
59 | // ensure that the dimensions are 4 values separated by space
|
---|
60 | const m =
|
---|
61 | /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
|
---|
62 | viewBox
|
---|
63 | );
|
---|
64 | if (m == null) {
|
---|
65 | return;
|
---|
66 | }
|
---|
67 | const left = Number.parseFloat(m[1]);
|
---|
68 | const top = Number.parseFloat(m[2]);
|
---|
69 | const width = Number.parseFloat(m[3]);
|
---|
70 | const height = Number.parseFloat(m[4]);
|
---|
71 |
|
---|
72 | // store the viewBox boundaries
|
---|
73 | viewBoxData = {
|
---|
74 | left,
|
---|
75 | top,
|
---|
76 | right: left + width,
|
---|
77 | bottom: top + height,
|
---|
78 | width,
|
---|
79 | height,
|
---|
80 | };
|
---|
81 | }
|
---|
82 |
|
---|
83 | // consider that any item with a transform attribute is visible
|
---|
84 | if (node.attributes.transform != null) {
|
---|
85 | return visitSkip;
|
---|
86 | }
|
---|
87 |
|
---|
88 | if (
|
---|
89 | node.name === 'path' &&
|
---|
90 | node.attributes.d != null &&
|
---|
91 | viewBoxData != null
|
---|
92 | ) {
|
---|
93 | const pathData = parsePathData(node.attributes.d);
|
---|
94 |
|
---|
95 | // consider that a M command within the viewBox is visible
|
---|
96 | let visible = false;
|
---|
97 | for (const pathDataItem of pathData) {
|
---|
98 | if (pathDataItem.command === 'M') {
|
---|
99 | const [x, y] = pathDataItem.args;
|
---|
100 | if (
|
---|
101 | x >= viewBoxData.left &&
|
---|
102 | x <= viewBoxData.right &&
|
---|
103 | y >= viewBoxData.top &&
|
---|
104 | y <= viewBoxData.bottom
|
---|
105 | ) {
|
---|
106 | visible = true;
|
---|
107 | }
|
---|
108 | }
|
---|
109 | }
|
---|
110 | if (visible) {
|
---|
111 | return;
|
---|
112 | }
|
---|
113 |
|
---|
114 | if (pathData.length === 2) {
|
---|
115 | // close the path too short for intersects()
|
---|
116 | pathData.push({ command: 'z', args: [] });
|
---|
117 | }
|
---|
118 |
|
---|
119 | const { left, top, width, height } = viewBoxData;
|
---|
120 | /**
|
---|
121 | * @type {Array<PathDataItem>}
|
---|
122 | */
|
---|
123 | const viewBoxPathData = [
|
---|
124 | { command: 'M', args: [left, top] },
|
---|
125 | { command: 'h', args: [width] },
|
---|
126 | { command: 'v', args: [height] },
|
---|
127 | { command: 'H', args: [left] },
|
---|
128 | { command: 'z', args: [] },
|
---|
129 | ];
|
---|
130 |
|
---|
131 | if (intersects(viewBoxPathData, pathData) === false) {
|
---|
132 | detachNodeFromParent(node, parentNode);
|
---|
133 | }
|
---|
134 | }
|
---|
135 | },
|
---|
136 | },
|
---|
137 | };
|
---|
138 | };
|
---|