@jbrowse/svgcanvas 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,780 @@
1
+ /**
2
+ * SVGCanvas v2.0.3
3
+ * Draw on SVG using Canvas's 2D Context API.
4
+ *
5
+ * Licensed under the MIT license:
6
+ * http://www.opensource.org/licenses/mit-license.php
7
+ *
8
+ * Original Authors: Kerry Liu, Zeno Zeng
9
+ * Copyright (c) 2014 Gliffy Inc.
10
+ * Copyright (c) 2021 Zeno Zeng
11
+ *
12
+ * Vendored and converted to ESM/TypeScript for pure ESM compatibility.
13
+ */
14
+ /* eslint-disable @typescript-eslint/no-explicit-any */
15
+ function toString(obj) {
16
+ if (!obj) {
17
+ return obj;
18
+ }
19
+ if (typeof obj === 'string') {
20
+ return obj;
21
+ }
22
+ return obj + '';
23
+ }
24
+ function format(str, args) {
25
+ const keys = Object.keys(args);
26
+ for (const key of keys) {
27
+ str = str.replace(new RegExp('\\{' + key + '\\}', 'gi'), args[key]);
28
+ }
29
+ return str;
30
+ }
31
+ function randomString(holder) {
32
+ if (!holder) {
33
+ throw new Error('cannot create a random attribute name for an undefined object');
34
+ }
35
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';
36
+ let randomstring = '';
37
+ do {
38
+ randomstring = '';
39
+ for (let i = 0; i < 12; i++) {
40
+ randomstring += chars[Math.floor(Math.random() * chars.length)];
41
+ }
42
+ } while (holder[randomstring]);
43
+ return randomstring;
44
+ }
45
+ function createNamedToNumberedLookup(items, radix = 10) {
46
+ const lookup = {};
47
+ const parts = items.split(',');
48
+ for (let i = 0; i < parts.length; i += 2) {
49
+ const entity = '&' + parts[i + 1] + ';';
50
+ const base10 = parseInt(parts[i], radix);
51
+ lookup[entity] = '&#' + base10 + ';';
52
+ }
53
+ lookup['\\xa0'] = '&#160;';
54
+ return lookup;
55
+ }
56
+ function getTextAnchor(textAlign) {
57
+ const mapping = {
58
+ left: 'start',
59
+ right: 'end',
60
+ center: 'middle',
61
+ start: 'start',
62
+ end: 'end',
63
+ };
64
+ return mapping[textAlign] || mapping.start;
65
+ }
66
+ function getDominantBaseline(textBaseline) {
67
+ const mapping = {
68
+ alphabetic: 'alphabetic',
69
+ hanging: 'hanging',
70
+ top: 'text-before-edge',
71
+ bottom: 'text-after-edge',
72
+ middle: 'central',
73
+ };
74
+ return mapping[textBaseline] || mapping.alphabetic;
75
+ }
76
+ const namedEntities = createNamedToNumberedLookup('50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
77
+ '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
78
+ '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
79
+ '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
80
+ '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
81
+ '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
82
+ '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
83
+ '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
84
+ '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
85
+ '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
86
+ 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
87
+ 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
88
+ 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
89
+ 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
90
+ 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
91
+ '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
92
+ '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
93
+ '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
94
+ '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
95
+ '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
96
+ 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
97
+ 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
98
+ 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
99
+ '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
100
+ '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
101
+ const STYLES = {
102
+ strokeStyle: {
103
+ svgAttr: 'stroke',
104
+ canvas: '#000000',
105
+ svg: 'none',
106
+ apply: 'stroke',
107
+ },
108
+ fillStyle: {
109
+ svgAttr: 'fill',
110
+ canvas: '#000000',
111
+ svg: null,
112
+ apply: 'fill',
113
+ },
114
+ lineCap: {
115
+ svgAttr: 'stroke-linecap',
116
+ canvas: 'butt',
117
+ svg: 'butt',
118
+ apply: 'stroke',
119
+ },
120
+ lineJoin: {
121
+ svgAttr: 'stroke-linejoin',
122
+ canvas: 'miter',
123
+ svg: 'miter',
124
+ apply: 'stroke',
125
+ },
126
+ miterLimit: {
127
+ svgAttr: 'stroke-miterlimit',
128
+ canvas: 10,
129
+ svg: 4,
130
+ apply: 'stroke',
131
+ },
132
+ lineWidth: {
133
+ svgAttr: 'stroke-width',
134
+ canvas: 1,
135
+ svg: 1,
136
+ apply: 'stroke',
137
+ },
138
+ globalAlpha: {
139
+ svgAttr: 'opacity',
140
+ canvas: 1,
141
+ svg: 1,
142
+ apply: 'fill stroke',
143
+ },
144
+ font: {
145
+ canvas: '10px sans-serif',
146
+ },
147
+ shadowColor: {
148
+ canvas: '#000000',
149
+ },
150
+ shadowOffsetX: {
151
+ canvas: 0,
152
+ },
153
+ shadowOffsetY: {
154
+ canvas: 0,
155
+ },
156
+ shadowBlur: {
157
+ canvas: 0,
158
+ },
159
+ textAlign: {
160
+ canvas: 'start',
161
+ },
162
+ textBaseline: {
163
+ canvas: 'alphabetic',
164
+ },
165
+ lineDash: {
166
+ svgAttr: 'stroke-dasharray',
167
+ canvas: [],
168
+ svg: null,
169
+ apply: 'stroke',
170
+ },
171
+ };
172
+ class CanvasGradient {
173
+ __root;
174
+ __ctx;
175
+ constructor(gradientNode, ctx) {
176
+ this.__root = gradientNode;
177
+ this.__ctx = ctx;
178
+ }
179
+ addColorStop(offset, color) {
180
+ const stop = this.__ctx.__createElement('stop');
181
+ stop.setAttribute('offset', String(offset));
182
+ if (toString(color).includes('rgba')) {
183
+ const regex = /rgba\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi;
184
+ const matches = regex.exec(color);
185
+ if (matches) {
186
+ stop.setAttribute('stop-color', format('rgb({r},{g},{b})', {
187
+ r: matches[1],
188
+ g: matches[2],
189
+ b: matches[3],
190
+ }));
191
+ stop.setAttribute('stop-opacity', matches[4]);
192
+ }
193
+ }
194
+ else {
195
+ stop.setAttribute('stop-color', toString(color));
196
+ }
197
+ this.__root.appendChild(stop);
198
+ }
199
+ }
200
+ class CanvasPattern {
201
+ __root;
202
+ __ctx;
203
+ constructor(pattern, ctx) {
204
+ this.__root = pattern;
205
+ this.__ctx = ctx;
206
+ }
207
+ }
208
+ export class Context {
209
+ width;
210
+ height;
211
+ enableMirroring;
212
+ canvas;
213
+ __document;
214
+ __ctx;
215
+ __canvas;
216
+ __root;
217
+ __ids;
218
+ __defs;
219
+ __currentElement;
220
+ __styleStack;
221
+ __groupStack;
222
+ __currentDefaultPath;
223
+ __currentPosition;
224
+ __transformMatrix;
225
+ __transformMatrixStack;
226
+ __currentElementsToStyle;
227
+ __options;
228
+ __id;
229
+ __fontUnderline;
230
+ __fontHref;
231
+ // Style properties
232
+ strokeStyle;
233
+ fillStyle;
234
+ lineCap;
235
+ lineJoin;
236
+ miterLimit;
237
+ lineWidth;
238
+ globalAlpha;
239
+ font;
240
+ shadowColor;
241
+ shadowOffsetX;
242
+ shadowOffsetY;
243
+ shadowBlur;
244
+ textAlign;
245
+ textBaseline;
246
+ lineDash;
247
+ constructor(o, height) {
248
+ const defaultOptions = { width: 500, height: 500, enableMirroring: false };
249
+ let options;
250
+ if (typeof o === 'number' && height !== undefined) {
251
+ options = { ...defaultOptions, width: o, height };
252
+ }
253
+ else if (!o) {
254
+ options = defaultOptions;
255
+ }
256
+ else if (typeof o === 'object') {
257
+ options = o;
258
+ }
259
+ else {
260
+ options = defaultOptions;
261
+ }
262
+ this.width = options.width || defaultOptions.width;
263
+ this.height = options.height || defaultOptions.height;
264
+ this.enableMirroring =
265
+ options.enableMirroring !== undefined
266
+ ? options.enableMirroring
267
+ : defaultOptions.enableMirroring;
268
+ this.canvas = this;
269
+ this.__document = options.document || document;
270
+ if (options.ctx) {
271
+ this.__ctx = options.ctx;
272
+ }
273
+ else {
274
+ this.__canvas = this.__document.createElement('canvas');
275
+ this.__ctx = this.__canvas.getContext('2d');
276
+ }
277
+ this.__setDefaultStyles();
278
+ this.__styleStack = [this.__getStyleState()];
279
+ this.__groupStack = [];
280
+ this.__root = this.__document.createElementNS('http://www.w3.org/2000/svg', 'svg');
281
+ this.__root.setAttribute('version', '1.1');
282
+ this.__root.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
283
+ this.__root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
284
+ this.__root.setAttribute('width', String(this.width));
285
+ this.__root.setAttribute('height', String(this.height));
286
+ this.__ids = {};
287
+ this.__defs = this.__document.createElementNS('http://www.w3.org/2000/svg', 'defs');
288
+ this.__root.appendChild(this.__defs);
289
+ this.__currentElement = this.__document.createElementNS('http://www.w3.org/2000/svg', 'g');
290
+ this.__root.appendChild(this.__currentElement);
291
+ this.__currentDefaultPath = '';
292
+ this.__currentPosition = {};
293
+ this.__transformMatrix = new DOMMatrix();
294
+ this.resetTransform();
295
+ this.__options = options;
296
+ this.__id = Math.random().toString(16).substring(2, 8);
297
+ }
298
+ __createElement(elementName, properties = {}, resetFill) {
299
+ const element = this.__document.createElementNS('http://www.w3.org/2000/svg', elementName);
300
+ if (resetFill) {
301
+ element.setAttribute('fill', 'none');
302
+ element.setAttribute('stroke', 'none');
303
+ }
304
+ for (const key of Object.keys(properties)) {
305
+ element.setAttribute(key, properties[key]);
306
+ }
307
+ return element;
308
+ }
309
+ __setDefaultStyles() {
310
+ for (const key of Object.keys(STYLES)) {
311
+ ;
312
+ this[key] = STYLES[key].canvas;
313
+ }
314
+ }
315
+ __applyStyleState(styleState) {
316
+ for (const key of Object.keys(styleState)) {
317
+ ;
318
+ this[key] = styleState[key];
319
+ }
320
+ }
321
+ __getStyleState() {
322
+ const styleState = {};
323
+ for (const key of Object.keys(STYLES)) {
324
+ styleState[key] = this[key];
325
+ }
326
+ return styleState;
327
+ }
328
+ __applyTransformation(element, matrix) {
329
+ const { a, b, c, d, e, f } = matrix || this.getTransform();
330
+ element.setAttribute('transform', `matrix(${a} ${b} ${c} ${d} ${e} ${f})`);
331
+ }
332
+ __applyStyleToCurrentElement(type) {
333
+ let currentElement = this.__currentElement;
334
+ const currentStyleGroup = this.__currentElementsToStyle;
335
+ if (currentStyleGroup) {
336
+ currentElement.setAttribute(type, '');
337
+ currentElement = currentStyleGroup.element;
338
+ for (const node of currentStyleGroup.children) {
339
+ node.setAttribute(type, '');
340
+ }
341
+ }
342
+ const keys = Object.keys(STYLES);
343
+ for (const key of keys) {
344
+ const style = STYLES[key];
345
+ const value = this[key];
346
+ if (style.apply) {
347
+ if (value instanceof CanvasPattern) {
348
+ if (value.__ctx) {
349
+ for (const node of Array.from(value.__ctx.__defs.childNodes)) {
350
+ const id = node.getAttribute('id');
351
+ if (id) {
352
+ this.__ids[id] = id;
353
+ this.__defs.appendChild(node);
354
+ }
355
+ }
356
+ }
357
+ currentElement.setAttribute(style.apply, format('url(#{id})', { id: value.__root.getAttribute('id') }));
358
+ }
359
+ else if (value instanceof CanvasGradient) {
360
+ currentElement.setAttribute(style.apply, format('url(#{id})', { id: value.__root.getAttribute('id') }));
361
+ }
362
+ else if (style.apply.includes(type) && style.svg !== value) {
363
+ if ((style.svgAttr === 'stroke' || style.svgAttr === 'fill') &&
364
+ value &&
365
+ value.includes('rgba')) {
366
+ const regex = /rgba\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi;
367
+ const matches = regex.exec(value);
368
+ if (matches) {
369
+ currentElement.setAttribute(style.svgAttr, format('rgb({r},{g},{b})', {
370
+ r: matches[1],
371
+ g: matches[2],
372
+ b: matches[3],
373
+ }));
374
+ let opacity = Number(matches[4]);
375
+ const globalAlpha = this.globalAlpha;
376
+ if (globalAlpha != null) {
377
+ opacity *= globalAlpha;
378
+ }
379
+ currentElement.setAttribute(style.svgAttr + '-opacity', String(opacity));
380
+ }
381
+ }
382
+ else {
383
+ let attr = style.svgAttr;
384
+ let val = value;
385
+ if (key === 'globalAlpha') {
386
+ attr = type + '-' + style.svgAttr;
387
+ if (currentElement.getAttribute(attr)) {
388
+ continue;
389
+ }
390
+ }
391
+ else if (key === 'lineWidth') {
392
+ const scale = this.__getTransformScale();
393
+ val = value * Math.max(scale.x, scale.y);
394
+ }
395
+ currentElement.setAttribute(attr, val);
396
+ }
397
+ }
398
+ }
399
+ }
400
+ }
401
+ __closestGroupOrSvg(node) {
402
+ node = node || this.__currentElement;
403
+ if (node.nodeName === 'g' || node.nodeName === 'svg') {
404
+ return node;
405
+ }
406
+ return this.__closestGroupOrSvg(node.parentNode);
407
+ }
408
+ getSerializedSvg(fixNamedEntities) {
409
+ let serialized = new XMLSerializer().serializeToString(this.__root);
410
+ const xmlns = /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi;
411
+ if (xmlns.test(serialized)) {
412
+ serialized = serialized.replace('xmlns="http://www.w3.org/2000/svg', 'xmlns:xlink="http://www.w3.org/1999/xlink');
413
+ }
414
+ if (fixNamedEntities) {
415
+ for (const key of Object.keys(namedEntities)) {
416
+ const regexp = new RegExp(key, 'gi');
417
+ if (regexp.test(serialized)) {
418
+ serialized = serialized.replace(regexp, namedEntities[key]);
419
+ }
420
+ }
421
+ }
422
+ return serialized;
423
+ }
424
+ getSvg() {
425
+ return this.__root;
426
+ }
427
+ save() {
428
+ const group = this.__createElement('g');
429
+ const parent = this.__closestGroupOrSvg();
430
+ this.__groupStack.push(parent);
431
+ parent.appendChild(group);
432
+ this.__currentElement = group;
433
+ const style = this.__getStyleState();
434
+ this.__styleStack.push(style);
435
+ if (!this.__transformMatrixStack) {
436
+ this.__transformMatrixStack = [];
437
+ }
438
+ this.__transformMatrixStack.push(this.getTransform());
439
+ }
440
+ restore() {
441
+ this.__currentElement = this.__groupStack.pop();
442
+ this.__currentElementsToStyle = undefined;
443
+ if (!this.__currentElement) {
444
+ this.__currentElement = this.__root.childNodes[1];
445
+ }
446
+ const state = this.__styleStack.pop();
447
+ this.__applyStyleState(state);
448
+ if (this.__transformMatrixStack && this.__transformMatrixStack.length > 0) {
449
+ this.setTransform(this.__transformMatrixStack.pop());
450
+ }
451
+ }
452
+ beginPath() {
453
+ this.__currentDefaultPath = '';
454
+ this.__currentPosition = {};
455
+ const path = this.__createElement('path', {}, true);
456
+ const parent = this.__closestGroupOrSvg();
457
+ parent.appendChild(path);
458
+ this.__currentElement = path;
459
+ }
460
+ __applyCurrentDefaultPath() {
461
+ const currentElement = this.__currentElement;
462
+ if (currentElement.nodeName === 'path') {
463
+ currentElement.setAttribute('d', this.__currentDefaultPath);
464
+ }
465
+ }
466
+ __addPathCommand(command) {
467
+ this.__currentDefaultPath += ' ';
468
+ this.__currentDefaultPath += command;
469
+ }
470
+ moveTo(x, y) {
471
+ if (this.__currentElement.nodeName !== 'path') {
472
+ this.beginPath();
473
+ }
474
+ this.__currentPosition = { x, y };
475
+ this.__addPathCommand(format('M {x} {y}', {
476
+ x: this.__matrixTransform(x, y).x,
477
+ y: this.__matrixTransform(x, y).y,
478
+ }));
479
+ }
480
+ closePath() {
481
+ if (this.__currentDefaultPath) {
482
+ this.__addPathCommand('Z');
483
+ }
484
+ }
485
+ lineTo(x, y) {
486
+ this.__currentPosition = { x, y };
487
+ if (this.__currentDefaultPath.includes('M')) {
488
+ this.__addPathCommand(format('L {x} {y}', {
489
+ x: this.__matrixTransform(x, y).x,
490
+ y: this.__matrixTransform(x, y).y,
491
+ }));
492
+ }
493
+ else {
494
+ this.__addPathCommand(format('M {x} {y}', {
495
+ x: this.__matrixTransform(x, y).x,
496
+ y: this.__matrixTransform(x, y).y,
497
+ }));
498
+ }
499
+ }
500
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
501
+ this.__currentPosition = { x, y };
502
+ this.__addPathCommand(format('C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}', {
503
+ cp1x: this.__matrixTransform(cp1x, cp1y).x,
504
+ cp1y: this.__matrixTransform(cp1x, cp1y).y,
505
+ cp2x: this.__matrixTransform(cp2x, cp2y).x,
506
+ cp2y: this.__matrixTransform(cp2x, cp2y).y,
507
+ x: this.__matrixTransform(x, y).x,
508
+ y: this.__matrixTransform(x, y).y,
509
+ }));
510
+ }
511
+ quadraticCurveTo(cpx, cpy, x, y) {
512
+ this.__currentPosition = { x, y };
513
+ this.__addPathCommand(format('Q {cpx} {cpy} {x} {y}', {
514
+ cpx: this.__matrixTransform(cpx, cpy).x,
515
+ cpy: this.__matrixTransform(cpx, cpy).y,
516
+ x: this.__matrixTransform(x, y).x,
517
+ y: this.__matrixTransform(x, y).y,
518
+ }));
519
+ }
520
+ stroke() {
521
+ if (this.__currentElement.nodeName === 'path') {
522
+ this.__currentElement.setAttribute('paint-order', 'fill stroke markers');
523
+ }
524
+ this.__applyCurrentDefaultPath();
525
+ this.__applyStyleToCurrentElement('stroke');
526
+ }
527
+ fill() {
528
+ if (this.__currentElement.nodeName === 'path') {
529
+ this.__currentElement.setAttribute('paint-order', 'stroke fill markers');
530
+ }
531
+ this.__applyCurrentDefaultPath();
532
+ this.__applyStyleToCurrentElement('fill');
533
+ }
534
+ rect(x, y, width, height) {
535
+ if (this.__currentElement.nodeName !== 'path') {
536
+ this.beginPath();
537
+ }
538
+ this.moveTo(x, y);
539
+ this.lineTo(x + width, y);
540
+ this.lineTo(x + width, y + height);
541
+ this.lineTo(x, y + height);
542
+ this.lineTo(x, y);
543
+ this.closePath();
544
+ }
545
+ __clearCanvas() {
546
+ const rootGroup = this.__root.childNodes[1];
547
+ this.__root.removeChild(rootGroup);
548
+ this.__currentElement = this.__document.createElementNS('http://www.w3.org/2000/svg', 'g');
549
+ this.__root.appendChild(this.__currentElement);
550
+ this.__groupStack = [];
551
+ }
552
+ fillRect(x, y, width, height) {
553
+ const { a, b, c, d, e, f } = this.getTransform();
554
+ if (JSON.stringify([a, b, c, d, e, f]) === JSON.stringify([1, 0, 0, 1, 0, 0])) {
555
+ if (x === 0 && y === 0 && width === this.width && height === this.height) {
556
+ this.__clearCanvas();
557
+ }
558
+ }
559
+ const rect = this.__createElement('rect', { x, y, width, height }, true);
560
+ const parent = this.__closestGroupOrSvg();
561
+ parent.appendChild(rect);
562
+ this.__currentElement = rect;
563
+ this.__applyTransformation(rect);
564
+ this.__applyStyleToCurrentElement('fill');
565
+ }
566
+ strokeRect(x, y, width, height) {
567
+ const rect = this.__createElement('rect', { x, y, width, height }, true);
568
+ const parent = this.__closestGroupOrSvg();
569
+ parent.appendChild(rect);
570
+ this.__currentElement = rect;
571
+ this.__applyTransformation(rect);
572
+ this.__applyStyleToCurrentElement('stroke');
573
+ }
574
+ clearRect(x, y, width, height) {
575
+ const { a, b, c, d, e, f } = this.getTransform();
576
+ if (JSON.stringify([a, b, c, d, e, f]) === JSON.stringify([1, 0, 0, 1, 0, 0])) {
577
+ if (x === 0 && y === 0 && width === this.width && height === this.height) {
578
+ this.__clearCanvas();
579
+ return;
580
+ }
581
+ }
582
+ const rect = this.__createElement('rect', { x, y, width, height, fill: '#FFFFFF' }, true);
583
+ this.__applyTransformation(rect);
584
+ const parent = this.__closestGroupOrSvg();
585
+ parent.appendChild(rect);
586
+ }
587
+ createLinearGradient(x1, y1, x2, y2) {
588
+ const grad = this.__createElement('linearGradient', {
589
+ id: randomString(this.__ids),
590
+ x1: x1 + 'px',
591
+ x2: x2 + 'px',
592
+ y1: y1 + 'px',
593
+ y2: y2 + 'px',
594
+ gradientUnits: 'userSpaceOnUse',
595
+ });
596
+ this.__defs.appendChild(grad);
597
+ return new CanvasGradient(grad, this);
598
+ }
599
+ createRadialGradient(x0, y0, r0, x1, y1, r1) {
600
+ const grad = this.__createElement('radialGradient', {
601
+ id: randomString(this.__ids),
602
+ cx: x1 + 'px',
603
+ cy: y1 + 'px',
604
+ r: r1 + 'px',
605
+ fx: x0 + 'px',
606
+ fy: y0 + 'px',
607
+ gradientUnits: 'userSpaceOnUse',
608
+ });
609
+ this.__defs.appendChild(grad);
610
+ return new CanvasGradient(grad, this);
611
+ }
612
+ __applyText(text, x, y, action) {
613
+ const el = document.createElement('span');
614
+ el.setAttribute('style', 'font:' + this.font);
615
+ const style = el.style;
616
+ const parent = this.__closestGroupOrSvg();
617
+ const textElement = this.__createElement('text', {
618
+ 'font-family': style.fontFamily,
619
+ 'font-size': style.fontSize,
620
+ 'font-style': style.fontStyle,
621
+ 'font-weight': style.fontWeight,
622
+ 'text-decoration': this.__fontUnderline,
623
+ x: x,
624
+ y: y,
625
+ 'text-anchor': getTextAnchor(this.textAlign),
626
+ 'dominant-baseline': getDominantBaseline(this.textBaseline),
627
+ }, true);
628
+ textElement.appendChild(this.__document.createTextNode(text));
629
+ this.__currentElement = textElement;
630
+ this.__applyTransformation(textElement);
631
+ this.__applyStyleToCurrentElement(action);
632
+ let finalElement = textElement;
633
+ if (this.__fontHref) {
634
+ const a = this.__createElement('a');
635
+ a.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', this.__fontHref);
636
+ a.appendChild(textElement);
637
+ finalElement = a;
638
+ }
639
+ parent.appendChild(finalElement);
640
+ }
641
+ fillText(text, x, y) {
642
+ this.__applyText(text, x, y, 'fill');
643
+ }
644
+ strokeText(text, x, y) {
645
+ this.__applyText(text, x, y, 'stroke');
646
+ }
647
+ measureText(text) {
648
+ this.__ctx.font = this.font;
649
+ return this.__ctx.measureText(text);
650
+ }
651
+ arc(x, y, radius, startAngle, endAngle, counterClockwise) {
652
+ if (startAngle === endAngle) {
653
+ return;
654
+ }
655
+ startAngle = startAngle % (2 * Math.PI);
656
+ endAngle = endAngle % (2 * Math.PI);
657
+ if (startAngle === endAngle) {
658
+ endAngle =
659
+ ((endAngle + 2 * Math.PI - 0.001 * (counterClockwise ? -1 : 1)) %
660
+ (2 * Math.PI));
661
+ }
662
+ const endX = x + radius * Math.cos(endAngle);
663
+ const endY = y + radius * Math.sin(endAngle);
664
+ const startX = x + radius * Math.cos(startAngle);
665
+ const startY = y + radius * Math.sin(startAngle);
666
+ const sweepFlag = counterClockwise ? 0 : 1;
667
+ let largeArcFlag = 0;
668
+ let diff = endAngle - startAngle;
669
+ if (diff < 0) {
670
+ diff += 2 * Math.PI;
671
+ }
672
+ if (counterClockwise) {
673
+ largeArcFlag = diff > Math.PI ? 0 : 1;
674
+ }
675
+ else {
676
+ largeArcFlag = diff > Math.PI ? 1 : 0;
677
+ }
678
+ const scaleX = Math.hypot(this.__transformMatrix.a, this.__transformMatrix.b);
679
+ const scaleY = Math.hypot(this.__transformMatrix.c, this.__transformMatrix.d);
680
+ this.lineTo(startX, startY);
681
+ this.__addPathCommand(format('A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}', {
682
+ rx: radius * scaleX,
683
+ ry: radius * scaleY,
684
+ xAxisRotation: 0,
685
+ largeArcFlag,
686
+ sweepFlag,
687
+ endX: this.__matrixTransform(endX, endY).x,
688
+ endY: this.__matrixTransform(endX, endY).y,
689
+ }));
690
+ this.__currentPosition = { x: endX, y: endY };
691
+ }
692
+ clip() {
693
+ const group = this.__closestGroupOrSvg();
694
+ const clipPath = this.__createElement('clipPath');
695
+ const id = randomString(this.__ids);
696
+ const newGroup = this.__createElement('g');
697
+ this.__applyCurrentDefaultPath();
698
+ group.removeChild(this.__currentElement);
699
+ clipPath.setAttribute('id', id);
700
+ clipPath.appendChild(this.__currentElement);
701
+ this.__defs.appendChild(clipPath);
702
+ group.setAttribute('clip-path', format('url(#{id})', { id }));
703
+ group.appendChild(newGroup);
704
+ this.__currentElement = newGroup;
705
+ }
706
+ setLineDash(dashArray) {
707
+ if (dashArray && dashArray.length > 0) {
708
+ this.lineDash = dashArray.join(',');
709
+ }
710
+ else {
711
+ this.lineDash = null;
712
+ }
713
+ }
714
+ setTransform(a, b, c, d, e, f) {
715
+ if (a instanceof DOMMatrix) {
716
+ this.__transformMatrix = new DOMMatrix([a.a, a.b, a.c, a.d, a.e, a.f]);
717
+ }
718
+ else {
719
+ this.__transformMatrix = new DOMMatrix([a, b, c, d, e, f]);
720
+ }
721
+ }
722
+ getTransform() {
723
+ const { a, b, c, d, e, f } = this.__transformMatrix;
724
+ return new DOMMatrix([a, b, c, d, e, f]);
725
+ }
726
+ resetTransform() {
727
+ this.setTransform(1, 0, 0, 1, 0, 0);
728
+ }
729
+ scale(x, y) {
730
+ if (y === undefined) {
731
+ y = x;
732
+ }
733
+ if (isNaN(x) || isNaN(y) || !isFinite(x) || !isFinite(y)) {
734
+ return;
735
+ }
736
+ const matrix = this.getTransform().scale(x, y);
737
+ this.setTransform(matrix);
738
+ }
739
+ rotate(angle) {
740
+ const matrix = this.getTransform().multiply(new DOMMatrix([
741
+ Math.cos(angle),
742
+ Math.sin(angle),
743
+ -Math.sin(angle),
744
+ Math.cos(angle),
745
+ 0,
746
+ 0,
747
+ ]));
748
+ this.setTransform(matrix);
749
+ }
750
+ translate(x, y) {
751
+ const matrix = this.getTransform().translate(x, y);
752
+ this.setTransform(matrix);
753
+ }
754
+ transform(a, b, c, d, e, f) {
755
+ const matrix = this.getTransform().multiply(new DOMMatrix([a, b, c, d, e, f]));
756
+ this.setTransform(matrix);
757
+ }
758
+ __matrixTransform(x, y) {
759
+ return new DOMPoint(x, y).matrixTransform(this.__transformMatrix);
760
+ }
761
+ __getTransformScale() {
762
+ return {
763
+ x: Math.hypot(this.__transformMatrix.a, this.__transformMatrix.b),
764
+ y: Math.hypot(this.__transformMatrix.c, this.__transformMatrix.d),
765
+ };
766
+ }
767
+ __getTransformRotation() {
768
+ return Math.atan2(this.__transformMatrix.b, this.__transformMatrix.a);
769
+ }
770
+ // Stubs for unimplemented methods
771
+ drawFocusRing() { }
772
+ createImageData() { }
773
+ putImageData() { }
774
+ globalCompositeOperation() { }
775
+ drawImage() { }
776
+ createPattern() {
777
+ return null;
778
+ }
779
+ }
780
+ //# sourceMappingURL=index.js.map