source: trip-planner-front/node_modules/svgo/plugins/_applyTransforms.js@ 6a3a178

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

initial commit

  • Property mode set to 100644
File size: 9.6 KB
Line 
1'use strict';
2
3// TODO implement as separate plugin
4
5const {
6 transformsMultiply,
7 transform2js,
8 transformArc,
9} = require('./_transforms.js');
10const { removeLeadingZero } = require('../lib/svgo/tools.js');
11const { referencesProps, attrsGroupsDefaults } = require('./_collections.js');
12
13const regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
14const defaultStrokeWidth = attrsGroupsDefaults.presentation['stroke-width'];
15
16/**
17 * Apply transformation(s) to the Path data.
18 *
19 * @param {Object} elem current element
20 * @param {Array} path input path data
21 * @param {Object} params whether to apply transforms to stroked lines and transform precision (used for stroke width)
22 * @return {Array} output path data
23 */
24const applyTransforms = (elem, pathData, params) => {
25 // if there are no 'stroke' attr and references to other objects such as
26 // gradiends or clip-path which are also subjects to transform.
27 if (
28 elem.attributes.transform == null ||
29 elem.attributes.transform === '' ||
30 // styles are not considered when applying transform
31 // can be fixed properly with new style engine
32 elem.attributes.style != null ||
33 Object.entries(elem.attributes).some(
34 ([name, value]) =>
35 referencesProps.includes(name) && value.includes('url(')
36 )
37 ) {
38 return;
39 }
40
41 const matrix = transformsMultiply(transform2js(elem.attributes.transform));
42 const stroke = elem.computedAttr('stroke');
43 const id = elem.computedAttr('id');
44 const transformPrecision = params.transformPrecision;
45
46 if (stroke && stroke != 'none') {
47 if (
48 !params.applyTransformsStroked ||
49 ((matrix.data[0] != matrix.data[3] ||
50 matrix.data[1] != -matrix.data[2]) &&
51 (matrix.data[0] != -matrix.data[3] || matrix.data[1] != matrix.data[2]))
52 )
53 return;
54
55 // "stroke-width" should be inside the part with ID, otherwise it can be overrided in <use>
56 if (id) {
57 let idElem = elem;
58 let hasStrokeWidth = false;
59
60 do {
61 if (idElem.attributes['stroke-width']) {
62 hasStrokeWidth = true;
63 }
64 } while (
65 idElem.attributes.id !== id &&
66 !hasStrokeWidth &&
67 (idElem = idElem.parentNode)
68 );
69
70 if (!hasStrokeWidth) return;
71 }
72
73 const scale = +Math.sqrt(
74 matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1]
75 ).toFixed(transformPrecision);
76
77 if (scale !== 1) {
78 const strokeWidth =
79 elem.computedAttr('stroke-width') || defaultStrokeWidth;
80
81 if (
82 elem.attributes['vector-effect'] == null ||
83 elem.attributes['vector-effect'] !== 'non-scaling-stroke'
84 ) {
85 if (elem.attributes['stroke-width'] != null) {
86 elem.attributes['stroke-width'] = elem.attributes['stroke-width']
87 .trim()
88 .replace(regNumericValues, (num) => removeLeadingZero(num * scale));
89 } else {
90 elem.attributes['stroke-width'] = strokeWidth.replace(
91 regNumericValues,
92 (num) => removeLeadingZero(num * scale)
93 );
94 }
95
96 if (elem.attributes['stroke-dashoffset'] != null) {
97 elem.attributes['stroke-dashoffset'] = elem.attributes[
98 'stroke-dashoffset'
99 ]
100 .trim()
101 .replace(regNumericValues, (num) => removeLeadingZero(num * scale));
102 }
103
104 if (elem.attributes['stroke-dasharray'] != null) {
105 elem.attributes['stroke-dasharray'] = elem.attributes[
106 'stroke-dasharray'
107 ]
108 .trim()
109 .replace(regNumericValues, (num) => removeLeadingZero(num * scale));
110 }
111 }
112 }
113 } else if (id) {
114 // Stroke and stroke-width can be redefined with <use>
115 return;
116 }
117
118 applyMatrixToPathData(pathData, matrix.data);
119
120 // remove transform attr
121 delete elem.attributes.transform;
122
123 return;
124};
125exports.applyTransforms = applyTransforms;
126
127const transformAbsolutePoint = (matrix, x, y) => {
128 const newX = matrix[0] * x + matrix[2] * y + matrix[4];
129 const newY = matrix[1] * x + matrix[3] * y + matrix[5];
130 return [newX, newY];
131};
132
133const transformRelativePoint = (matrix, x, y) => {
134 const newX = matrix[0] * x + matrix[2] * y;
135 const newY = matrix[1] * x + matrix[3] * y;
136 return [newX, newY];
137};
138
139const applyMatrixToPathData = (pathData, matrix) => {
140 const start = [0, 0];
141 const cursor = [0, 0];
142
143 for (const pathItem of pathData) {
144 let { command, args } = pathItem;
145
146 // moveto (x y)
147 if (command === 'M') {
148 cursor[0] = args[0];
149 cursor[1] = args[1];
150 start[0] = cursor[0];
151 start[1] = cursor[1];
152 const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]);
153 args[0] = x;
154 args[1] = y;
155 }
156 if (command === 'm') {
157 cursor[0] += args[0];
158 cursor[1] += args[1];
159 start[0] = cursor[0];
160 start[1] = cursor[1];
161 const [x, y] = transformRelativePoint(matrix, args[0], args[1]);
162 args[0] = x;
163 args[1] = y;
164 }
165
166 // horizontal lineto (x)
167 // convert to lineto to handle two-dimentional transforms
168 if (command === 'H') {
169 command = 'L';
170 args = [args[0], cursor[1]];
171 }
172 if (command === 'h') {
173 command = 'l';
174 args = [args[0], 0];
175 }
176
177 // vertical lineto (y)
178 // convert to lineto to handle two-dimentional transforms
179 if (command === 'V') {
180 command = 'L';
181 args = [cursor[0], args[0]];
182 }
183 if (command === 'v') {
184 command = 'l';
185 args = [0, args[0]];
186 }
187
188 // lineto (x y)
189 if (command === 'L') {
190 cursor[0] = args[0];
191 cursor[1] = args[1];
192 const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]);
193 args[0] = x;
194 args[1] = y;
195 }
196 if (command === 'l') {
197 cursor[0] += args[0];
198 cursor[1] += args[1];
199 const [x, y] = transformRelativePoint(matrix, args[0], args[1]);
200 args[0] = x;
201 args[1] = y;
202 }
203
204 // curveto (x1 y1 x2 y2 x y)
205 if (command === 'C') {
206 cursor[0] = args[4];
207 cursor[1] = args[5];
208 const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]);
209 const [x2, y2] = transformAbsolutePoint(matrix, args[2], args[3]);
210 const [x, y] = transformAbsolutePoint(matrix, args[4], args[5]);
211 args[0] = x1;
212 args[1] = y1;
213 args[2] = x2;
214 args[3] = y2;
215 args[4] = x;
216 args[5] = y;
217 }
218 if (command === 'c') {
219 cursor[0] += args[4];
220 cursor[1] += args[5];
221 const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]);
222 const [x2, y2] = transformRelativePoint(matrix, args[2], args[3]);
223 const [x, y] = transformRelativePoint(matrix, args[4], args[5]);
224 args[0] = x1;
225 args[1] = y1;
226 args[2] = x2;
227 args[3] = y2;
228 args[4] = x;
229 args[5] = y;
230 }
231
232 // smooth curveto (x2 y2 x y)
233 if (command === 'S') {
234 cursor[0] = args[2];
235 cursor[1] = args[3];
236 const [x2, y2] = transformAbsolutePoint(matrix, args[0], args[1]);
237 const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]);
238 args[0] = x2;
239 args[1] = y2;
240 args[2] = x;
241 args[3] = y;
242 }
243 if (command === 's') {
244 cursor[0] += args[2];
245 cursor[1] += args[3];
246 const [x2, y2] = transformRelativePoint(matrix, args[0], args[1]);
247 const [x, y] = transformRelativePoint(matrix, args[2], args[3]);
248 args[0] = x2;
249 args[1] = y2;
250 args[2] = x;
251 args[3] = y;
252 }
253
254 // quadratic Bézier curveto (x1 y1 x y)
255 if (command === 'Q') {
256 cursor[0] = args[2];
257 cursor[1] = args[3];
258 const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]);
259 const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]);
260 args[0] = x1;
261 args[1] = y1;
262 args[2] = x;
263 args[3] = y;
264 }
265 if (command === 'q') {
266 cursor[0] += args[2];
267 cursor[1] += args[3];
268 const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]);
269 const [x, y] = transformRelativePoint(matrix, args[2], args[3]);
270 args[0] = x1;
271 args[1] = y1;
272 args[2] = x;
273 args[3] = y;
274 }
275
276 // smooth quadratic Bézier curveto (x y)
277 if (command === 'T') {
278 cursor[0] = args[0];
279 cursor[1] = args[1];
280 const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]);
281 args[0] = x;
282 args[1] = y;
283 }
284 if (command === 't') {
285 cursor[0] += args[0];
286 cursor[1] += args[1];
287 const [x, y] = transformRelativePoint(matrix, args[0], args[1]);
288 args[0] = x;
289 args[1] = y;
290 }
291
292 // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
293 if (command === 'A') {
294 transformArc(cursor, args, matrix);
295 cursor[0] = args[5];
296 cursor[1] = args[6];
297 // reduce number of digits in rotation angle
298 if (Math.abs(args[2]) > 80) {
299 const a = args[0];
300 const rotation = args[2];
301 args[0] = args[1];
302 args[1] = a;
303 args[2] = rotation + (rotation > 0 ? -90 : 90);
304 }
305 const [x, y] = transformAbsolutePoint(matrix, args[5], args[6]);
306 args[5] = x;
307 args[6] = y;
308 }
309 if (command === 'a') {
310 transformArc([0, 0], args, matrix);
311 cursor[0] += args[5];
312 cursor[1] += args[6];
313 // reduce number of digits in rotation angle
314 if (Math.abs(args[2]) > 80) {
315 const a = args[0];
316 const rotation = args[2];
317 args[0] = args[1];
318 args[1] = a;
319 args[2] = rotation + (rotation > 0 ? -90 : 90);
320 }
321 const [x, y] = transformRelativePoint(matrix, args[5], args[6]);
322 args[5] = x;
323 args[6] = y;
324 }
325
326 // closepath
327 if (command === 'z' || command === 'Z') {
328 cursor[0] = start[0];
329 cursor[1] = start[1];
330 }
331
332 pathItem.command = command;
333 pathItem.args = args;
334 }
335};
Note: See TracBrowser for help on using the repository browser.