source: trip-planner-front/node_modules/svgo/plugins/convertShapeToPath.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 5.8 KB
Line 
1'use strict';
2
3/**
4 * @typedef {import('../lib/types').PathDataItem} PathDataItem
5 */
6
7const { stringifyPathData } = require('../lib/path.js');
8const { detachNodeFromParent } = require('../lib/xast.js');
9
10exports.name = 'convertShapeToPath';
11exports.type = 'visitor';
12exports.active = true;
13exports.description = 'converts basic shapes to more compact path form';
14
15const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
16
17/**
18 * Converts basic shape to more compact path.
19 * It also allows further optimizations like
20 * combining paths with similar attributes.
21 *
22 * @see https://www.w3.org/TR/SVG11/shapes.html
23 *
24 * @author Lev Solntsev
25 *
26 * @type {import('../lib/types').Plugin<{
27 * convertArcs?: boolean,
28 * floatPrecision?: number
29 * }>}
30 */
31exports.fn = (root, params) => {
32 const { convertArcs = false, floatPrecision: precision } = params;
33
34 return {
35 element: {
36 enter: (node, parentNode) => {
37 // convert rect to path
38 if (
39 node.name === 'rect' &&
40 node.attributes.width != null &&
41 node.attributes.height != null &&
42 node.attributes.rx == null &&
43 node.attributes.ry == null
44 ) {
45 const x = Number(node.attributes.x || '0');
46 const y = Number(node.attributes.y || '0');
47 const width = Number(node.attributes.width);
48 const height = Number(node.attributes.height);
49 // Values like '100%' compute to NaN, thus running after
50 // cleanupNumericValues when 'px' units has already been removed.
51 // TODO: Calculate sizes from % and non-px units if possible.
52 if (Number.isNaN(x - y + width - height)) return;
53 /**
54 * @type {Array<PathDataItem>}
55 */
56 const pathData = [
57 { command: 'M', args: [x, y] },
58 { command: 'H', args: [x + width] },
59 { command: 'V', args: [y + height] },
60 { command: 'H', args: [x] },
61 { command: 'z', args: [] },
62 ];
63 node.name = 'path';
64 node.attributes.d = stringifyPathData({ pathData, precision });
65 delete node.attributes.x;
66 delete node.attributes.y;
67 delete node.attributes.width;
68 delete node.attributes.height;
69 }
70
71 // convert line to path
72 if (node.name === 'line') {
73 const x1 = Number(node.attributes.x1 || '0');
74 const y1 = Number(node.attributes.y1 || '0');
75 const x2 = Number(node.attributes.x2 || '0');
76 const y2 = Number(node.attributes.y2 || '0');
77 if (Number.isNaN(x1 - y1 + x2 - y2)) return;
78 /**
79 * @type {Array<PathDataItem>}
80 */
81 const pathData = [
82 { command: 'M', args: [x1, y1] },
83 { command: 'L', args: [x2, y2] },
84 ];
85 node.name = 'path';
86 node.attributes.d = stringifyPathData({ pathData, precision });
87 delete node.attributes.x1;
88 delete node.attributes.y1;
89 delete node.attributes.x2;
90 delete node.attributes.y2;
91 }
92
93 // convert polyline and polygon to path
94 if (
95 (node.name === 'polyline' || node.name === 'polygon') &&
96 node.attributes.points != null
97 ) {
98 const coords = (node.attributes.points.match(regNumber) || []).map(
99 Number
100 );
101 if (coords.length < 4) {
102 detachNodeFromParent(node, parentNode);
103 return;
104 }
105 /**
106 * @type {Array<PathDataItem>}
107 */
108 const pathData = [];
109 for (let i = 0; i < coords.length; i += 2) {
110 pathData.push({
111 command: i === 0 ? 'M' : 'L',
112 args: coords.slice(i, i + 2),
113 });
114 }
115 if (node.name === 'polygon') {
116 pathData.push({ command: 'z', args: [] });
117 }
118 node.name = 'path';
119 node.attributes.d = stringifyPathData({ pathData, precision });
120 delete node.attributes.points;
121 }
122
123 // optionally convert circle
124 if (node.name === 'circle' && convertArcs) {
125 const cx = Number(node.attributes.cx || '0');
126 const cy = Number(node.attributes.cy || '0');
127 const r = Number(node.attributes.r || '0');
128 if (Number.isNaN(cx - cy + r)) {
129 return;
130 }
131 /**
132 * @type {Array<PathDataItem>}
133 */
134 const pathData = [
135 { command: 'M', args: [cx, cy - r] },
136 { command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
137 { command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
138 { command: 'z', args: [] },
139 ];
140 node.name = 'path';
141 node.attributes.d = stringifyPathData({ pathData, precision });
142 delete node.attributes.cx;
143 delete node.attributes.cy;
144 delete node.attributes.r;
145 }
146
147 // optionally covert ellipse
148 if (node.name === 'ellipse' && convertArcs) {
149 const ecx = Number(node.attributes.cx || '0');
150 const ecy = Number(node.attributes.cy || '0');
151 const rx = Number(node.attributes.rx || '0');
152 const ry = Number(node.attributes.ry || '0');
153 if (Number.isNaN(ecx - ecy + rx - ry)) {
154 return;
155 }
156 /**
157 * @type {Array<PathDataItem>}
158 */
159 const pathData = [
160 { command: 'M', args: [ecx, ecy - ry] },
161 { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
162 { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
163 { command: 'z', args: [] },
164 ];
165 node.name = 'path';
166 node.attributes.d = stringifyPathData({ pathData, precision });
167 delete node.attributes.cx;
168 delete node.attributes.cy;
169 delete node.attributes.rx;
170 delete node.attributes.ry;
171 }
172 },
173 },
174 };
175};
Note: See TracBrowser for help on using the repository browser.