@jbrowse/svgcanvas 6.5.0 → 8.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/context.test.js +67 -0
- package/dist/context.test.js.map +1 -0
- package/dist/index.d.ts +12 -56
- package/dist/index.js +149 -531
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/context.test.ts +71 -0
- package/src/index.ts +162 -658
- package/dist/drawImage.test.js +0 -76
- package/dist/drawImage.test.js.map +0 -1
- package/src/drawImage.test.ts +0 -81
- /package/dist/{drawImage.test.d.ts → context.test.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -9,50 +9,10 @@
|
|
|
9
9
|
* Copyright (c) 2014 Gliffy Inc.
|
|
10
10
|
* Copyright (c) 2021 Zeno Zeng
|
|
11
11
|
*
|
|
12
|
-
* Vendored
|
|
12
|
+
* Vendored, converted to ESM/TypeScript, and cut down to the calls the
|
|
13
|
+
* renderers make: rectangles, paths, arcs and glyphs.
|
|
13
14
|
*/
|
|
14
15
|
/* 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'] = ' ';
|
|
54
|
-
return lookup;
|
|
55
|
-
}
|
|
56
16
|
function getTextAnchor(textAlign) {
|
|
57
17
|
const mapping = {
|
|
58
18
|
left: 'start',
|
|
@@ -74,45 +34,18 @@ function getDominantBaseline(textBaseline) {
|
|
|
74
34
|
return mapping[textBaseline] || mapping.alphabetic;
|
|
75
35
|
}
|
|
76
36
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* dropping the four below takes ~22% off a sequence-logo figure.
|
|
37
|
+
* SVG's initial values for text presentation attributes. Omitting them from
|
|
38
|
+
* every glyph takes ~22% off a sequence-logo figure.
|
|
80
39
|
*/
|
|
81
40
|
const TEXT_ATTR_DEFAULTS = {
|
|
82
41
|
'font-style': 'normal',
|
|
83
42
|
'font-weight': 'normal',
|
|
84
|
-
'font-variant': 'normal',
|
|
85
43
|
// the initial value is `auto`, which resolves to the alphabetic baseline
|
|
86
44
|
'dominant-baseline': 'alphabetic',
|
|
87
45
|
};
|
|
88
46
|
function omitDefault(attr, value) {
|
|
89
47
|
return value === TEXT_ATTR_DEFAULTS[attr] ? undefined : value;
|
|
90
48
|
}
|
|
91
|
-
const namedEntities = createNamedToNumberedLookup('50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
|
|
92
|
-
'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
|
|
93
|
-
'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
|
|
94
|
-
'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
|
|
95
|
-
'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
|
|
96
|
-
'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
|
|
97
|
-
'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
|
|
98
|
-
'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
|
|
99
|
-
'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
|
|
100
|
-
'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
|
|
101
|
-
'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
|
|
102
|
-
'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
|
|
103
|
-
't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
|
|
104
|
-
'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
|
|
105
|
-
'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
|
|
106
|
-
'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
|
|
107
|
-
'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
|
|
108
|
-
'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
|
|
109
|
-
'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
|
|
110
|
-
'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
|
|
111
|
-
'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
|
|
112
|
-
'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
|
|
113
|
-
'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
|
|
114
|
-
'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
|
|
115
|
-
'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
|
|
116
49
|
const STYLES = {
|
|
117
50
|
strokeStyle: {
|
|
118
51
|
svgAttr: 'stroke',
|
|
@@ -126,111 +59,39 @@ const STYLES = {
|
|
|
126
59
|
svg: null,
|
|
127
60
|
apply: 'fill',
|
|
128
61
|
},
|
|
129
|
-
lineCap: {
|
|
130
|
-
svgAttr: 'stroke-linecap',
|
|
131
|
-
canvas: 'butt',
|
|
132
|
-
svg: 'butt',
|
|
133
|
-
apply: 'stroke',
|
|
134
|
-
},
|
|
135
|
-
lineJoin: {
|
|
136
|
-
svgAttr: 'stroke-linejoin',
|
|
137
|
-
canvas: 'miter',
|
|
138
|
-
svg: 'miter',
|
|
139
|
-
apply: 'stroke',
|
|
140
|
-
},
|
|
141
|
-
miterLimit: {
|
|
142
|
-
svgAttr: 'stroke-miterlimit',
|
|
143
|
-
canvas: 10,
|
|
144
|
-
svg: 4,
|
|
145
|
-
apply: 'stroke',
|
|
146
|
-
},
|
|
147
62
|
lineWidth: {
|
|
148
63
|
svgAttr: 'stroke-width',
|
|
149
64
|
canvas: 1,
|
|
150
65
|
svg: 1,
|
|
151
66
|
apply: 'stroke',
|
|
152
67
|
},
|
|
153
|
-
globalAlpha: {
|
|
154
|
-
svgAttr: 'opacity',
|
|
155
|
-
canvas: 1,
|
|
156
|
-
svg: 1,
|
|
157
|
-
apply: 'fill stroke',
|
|
158
|
-
},
|
|
159
68
|
font: {
|
|
160
69
|
canvas: '10px sans-serif',
|
|
161
70
|
},
|
|
162
|
-
shadowColor: {
|
|
163
|
-
canvas: '#000000',
|
|
164
|
-
},
|
|
165
|
-
shadowOffsetX: {
|
|
166
|
-
canvas: 0,
|
|
167
|
-
},
|
|
168
|
-
shadowOffsetY: {
|
|
169
|
-
canvas: 0,
|
|
170
|
-
},
|
|
171
|
-
shadowBlur: {
|
|
172
|
-
canvas: 0,
|
|
173
|
-
},
|
|
174
71
|
textAlign: {
|
|
175
72
|
canvas: 'start',
|
|
176
73
|
},
|
|
177
74
|
textBaseline: {
|
|
178
75
|
canvas: 'alphabetic',
|
|
179
76
|
},
|
|
77
|
+
// null on both sides so the canvas and svg defaults compare equal and strokes
|
|
78
|
+
// carry no empty stroke-dasharray
|
|
180
79
|
lineDash: {
|
|
181
80
|
svgAttr: 'stroke-dasharray',
|
|
182
|
-
canvas:
|
|
81
|
+
canvas: null,
|
|
183
82
|
svg: null,
|
|
184
83
|
apply: 'stroke',
|
|
185
84
|
},
|
|
186
85
|
};
|
|
187
|
-
|
|
188
|
-
__root;
|
|
189
|
-
__ctx;
|
|
190
|
-
constructor(gradientNode, ctx) {
|
|
191
|
-
this.__root = gradientNode;
|
|
192
|
-
this.__ctx = ctx;
|
|
193
|
-
}
|
|
194
|
-
addColorStop(offset, color) {
|
|
195
|
-
const stop = this.__ctx.__createElement('stop');
|
|
196
|
-
stop.setAttribute('offset', String(offset));
|
|
197
|
-
if (toString(color).includes('rgba')) {
|
|
198
|
-
const regex = /rgba\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi;
|
|
199
|
-
const matches = regex.exec(color);
|
|
200
|
-
if (matches) {
|
|
201
|
-
stop.setAttribute('stop-color', format('rgb({r},{g},{b})', {
|
|
202
|
-
r: matches[1],
|
|
203
|
-
g: matches[2],
|
|
204
|
-
b: matches[3],
|
|
205
|
-
}));
|
|
206
|
-
stop.setAttribute('stop-opacity', matches[4]);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
stop.setAttribute('stop-color', toString(color));
|
|
211
|
-
}
|
|
212
|
-
this.__root.appendChild(stop);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
class CanvasPattern {
|
|
216
|
-
__root;
|
|
217
|
-
__ctx;
|
|
218
|
-
constructor(pattern, ctx) {
|
|
219
|
-
this.__root = pattern;
|
|
220
|
-
this.__ctx = ctx;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
86
|
+
const rgbaRegex = /rgba\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d?\.?\d*)\s*\)/i;
|
|
223
87
|
export class Context {
|
|
224
88
|
width;
|
|
225
89
|
height;
|
|
226
|
-
enableMirroring;
|
|
227
90
|
canvas;
|
|
228
91
|
__document;
|
|
229
92
|
__ctx;
|
|
230
93
|
__canvas;
|
|
231
94
|
__root;
|
|
232
|
-
__ids;
|
|
233
|
-
__defs;
|
|
234
95
|
__currentElement;
|
|
235
96
|
__styleStack;
|
|
236
97
|
__groupStack;
|
|
@@ -238,48 +99,19 @@ export class Context {
|
|
|
238
99
|
__currentPosition;
|
|
239
100
|
__transformMatrix;
|
|
240
101
|
__transformMatrixStack;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
__fontUnderline;
|
|
245
|
-
__fontHref;
|
|
246
|
-
// Style properties
|
|
102
|
+
// parsed fonts keyed by `font` string; each parse costs a DOM element and a
|
|
103
|
+
// CSS parse, and exports ask per glyph
|
|
104
|
+
__fontCache = new Map();
|
|
247
105
|
strokeStyle;
|
|
248
106
|
fillStyle;
|
|
249
|
-
lineCap;
|
|
250
|
-
lineJoin;
|
|
251
|
-
miterLimit;
|
|
252
107
|
lineWidth;
|
|
253
|
-
globalAlpha;
|
|
254
108
|
font;
|
|
255
|
-
shadowColor;
|
|
256
|
-
shadowOffsetX;
|
|
257
|
-
shadowOffsetY;
|
|
258
|
-
shadowBlur;
|
|
259
109
|
textAlign;
|
|
260
110
|
textBaseline;
|
|
261
111
|
lineDash;
|
|
262
|
-
constructor(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (typeof o === 'number' && height !== undefined) {
|
|
266
|
-
options = { ...defaultOptions, width: o, height };
|
|
267
|
-
}
|
|
268
|
-
else if (!o) {
|
|
269
|
-
options = defaultOptions;
|
|
270
|
-
}
|
|
271
|
-
else if (typeof o === 'object') {
|
|
272
|
-
options = o;
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
options = defaultOptions;
|
|
276
|
-
}
|
|
277
|
-
this.width = options.width || defaultOptions.width;
|
|
278
|
-
this.height = options.height || defaultOptions.height;
|
|
279
|
-
this.enableMirroring =
|
|
280
|
-
options.enableMirroring !== undefined
|
|
281
|
-
? options.enableMirroring
|
|
282
|
-
: defaultOptions.enableMirroring;
|
|
112
|
+
constructor(width, height, options = {}) {
|
|
113
|
+
this.width = width;
|
|
114
|
+
this.height = height;
|
|
283
115
|
this.canvas = this;
|
|
284
116
|
this.__document = options.document || document;
|
|
285
117
|
if (options.ctx) {
|
|
@@ -295,32 +127,27 @@ export class Context {
|
|
|
295
127
|
this.__root = this.__document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
296
128
|
this.__root.setAttribute('version', '1.1');
|
|
297
129
|
this.__root.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
298
|
-
this.__root.
|
|
299
|
-
this.__root.setAttribute('
|
|
300
|
-
this.__root.setAttribute('height', String(this.height));
|
|
301
|
-
this.__ids = {};
|
|
302
|
-
this.__defs = this.__document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
303
|
-
this.__root.appendChild(this.__defs);
|
|
130
|
+
this.__root.setAttribute('width', String(width));
|
|
131
|
+
this.__root.setAttribute('height', String(height));
|
|
304
132
|
this.__currentElement = this.__document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
305
133
|
this.__root.appendChild(this.__currentElement);
|
|
306
134
|
this.__currentDefaultPath = '';
|
|
307
135
|
this.__currentPosition = {};
|
|
308
136
|
this.__transformMatrix = new DOMMatrix();
|
|
309
137
|
this.resetTransform();
|
|
310
|
-
this.__options = options;
|
|
311
|
-
this.__id = Math.random().toString(16).substring(2, 8);
|
|
312
138
|
}
|
|
313
139
|
__createElement(elementName, properties = {}, resetFill) {
|
|
314
140
|
const element = this.__document.createElementNS('http://www.w3.org/2000/svg', elementName);
|
|
315
141
|
if (resetFill) {
|
|
142
|
+
// placeholders: fill()/stroke() overwrite their own attribute, and
|
|
143
|
+
// __applyStyleToCurrentElement drops the other when it is the svg default
|
|
316
144
|
element.setAttribute('fill', 'none');
|
|
317
145
|
element.setAttribute('stroke', 'none');
|
|
318
146
|
}
|
|
319
147
|
for (const key of Object.keys(properties)) {
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
// text-decoration="undefined" -- invalid, and ~28 wasted bytes per letter
|
|
148
|
+
// __applyText passes optional attributes through unconditionally; skipping
|
|
149
|
+
// unset ones keeps an invalid text-decoration="undefined" (~28 bytes) off
|
|
150
|
+
// every glyph
|
|
324
151
|
const value = properties[key];
|
|
325
152
|
if (value !== undefined && value !== null) {
|
|
326
153
|
element.setAttribute(key, value);
|
|
@@ -347,77 +174,59 @@ export class Context {
|
|
|
347
174
|
}
|
|
348
175
|
return styleState;
|
|
349
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* A pure translation folds into the element's x/y, saving ~35 bytes per
|
|
179
|
+
* glyph. Any other transform, such as a sequence-logo letter's scale, is
|
|
180
|
+
* written as a matrix.
|
|
181
|
+
*/
|
|
350
182
|
__applyTransformation(element, matrix) {
|
|
351
183
|
const { a, b, c, d, e, f } = matrix || this.getTransform();
|
|
184
|
+
if (a === 1 &&
|
|
185
|
+
b === 0 &&
|
|
186
|
+
c === 0 &&
|
|
187
|
+
d === 1 &&
|
|
188
|
+
element.hasAttribute('x') &&
|
|
189
|
+
element.hasAttribute('y')) {
|
|
190
|
+
if (e !== 0) {
|
|
191
|
+
element.setAttribute('x', String(Number(element.getAttribute('x')) + e));
|
|
192
|
+
}
|
|
193
|
+
if (f !== 0) {
|
|
194
|
+
element.setAttribute('y', String(Number(element.getAttribute('y')) + f));
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
352
198
|
element.setAttribute('transform', `matrix(${a} ${b} ${c} ${d} ${e} ${f})`);
|
|
353
199
|
}
|
|
354
200
|
__applyStyleToCurrentElement(type) {
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
if (currentStyleGroup) {
|
|
358
|
-
currentElement.setAttribute(type, '');
|
|
359
|
-
currentElement = currentStyleGroup.element;
|
|
360
|
-
for (const node of currentStyleGroup.children) {
|
|
361
|
-
node.setAttribute(type, '');
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
const keys = Object.keys(STYLES);
|
|
365
|
-
for (const key of keys) {
|
|
201
|
+
const currentElement = this.__currentElement;
|
|
202
|
+
for (const key of Object.keys(STYLES)) {
|
|
366
203
|
const style = STYLES[key];
|
|
367
204
|
const value = this[key];
|
|
368
|
-
if (style.apply) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
else if (style.apply.includes(type) && style.svg !== value) {
|
|
385
|
-
if ((style.svgAttr === 'stroke' || style.svgAttr === 'fill') &&
|
|
386
|
-
value &&
|
|
387
|
-
value.includes('rgba')) {
|
|
388
|
-
const regex = /rgba\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi;
|
|
389
|
-
const matches = regex.exec(value);
|
|
390
|
-
if (matches) {
|
|
391
|
-
currentElement.setAttribute(style.svgAttr, format('rgb({r},{g},{b})', {
|
|
392
|
-
r: matches[1],
|
|
393
|
-
g: matches[2],
|
|
394
|
-
b: matches[3],
|
|
395
|
-
}));
|
|
396
|
-
let opacity = Number(matches[4]);
|
|
397
|
-
const globalAlpha = this.globalAlpha;
|
|
398
|
-
if (globalAlpha != null) {
|
|
399
|
-
opacity *= globalAlpha;
|
|
400
|
-
}
|
|
401
|
-
currentElement.setAttribute(style.svgAttr + '-opacity', String(opacity));
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
let attr = style.svgAttr;
|
|
406
|
-
let val = value;
|
|
407
|
-
if (key === 'globalAlpha') {
|
|
408
|
-
attr = type + '-' + style.svgAttr;
|
|
409
|
-
if (currentElement.getAttribute(attr)) {
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
else if (key === 'lineWidth') {
|
|
414
|
-
const scale = this.__getTransformScale();
|
|
415
|
-
val = value * Math.max(scale.x, scale.y);
|
|
416
|
-
}
|
|
417
|
-
currentElement.setAttribute(attr, val);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
205
|
+
if (!style.apply?.includes(type) || style.svg === value) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const matches = (style.svgAttr === 'stroke' || style.svgAttr === 'fill') &&
|
|
209
|
+
typeof value === 'string' &&
|
|
210
|
+
value.includes('rgba')
|
|
211
|
+
? rgbaRegex.exec(value)
|
|
212
|
+
: null;
|
|
213
|
+
if (matches) {
|
|
214
|
+
// SVG has no rgba(); the alpha goes in the -opacity attribute
|
|
215
|
+
currentElement.setAttribute(style.svgAttr, `rgb(${matches[1]},${matches[2]},${matches[3]})`);
|
|
216
|
+
currentElement.setAttribute(`${style.svgAttr}-opacity`, matches[4]);
|
|
217
|
+
}
|
|
218
|
+
else if (key === 'lineWidth') {
|
|
219
|
+
const scale = this.__getTransformScale();
|
|
220
|
+
currentElement.setAttribute(style.svgAttr, String(value * Math.max(scale.x, scale.y)));
|
|
420
221
|
}
|
|
222
|
+
else {
|
|
223
|
+
currentElement.setAttribute(style.svgAttr, value);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// svg defaults stroke to none, so a fill drops stroke="none". Fill defaults
|
|
227
|
+
// to black, so a stroked element keeps fill="none".
|
|
228
|
+
if (type === 'fill' && currentElement.getAttribute('stroke') === 'none') {
|
|
229
|
+
currentElement.removeAttribute('stroke');
|
|
421
230
|
}
|
|
422
231
|
}
|
|
423
232
|
__closestGroupOrSvg(node) {
|
|
@@ -427,22 +236,6 @@ export class Context {
|
|
|
427
236
|
}
|
|
428
237
|
return this.__closestGroupOrSvg(node.parentNode);
|
|
429
238
|
}
|
|
430
|
-
getSerializedSvg(fixNamedEntities) {
|
|
431
|
-
let serialized = new XMLSerializer().serializeToString(this.__root);
|
|
432
|
-
const xmlns = /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi;
|
|
433
|
-
if (xmlns.test(serialized)) {
|
|
434
|
-
serialized = serialized.replace('xmlns="http://www.w3.org/2000/svg', 'xmlns:xlink="http://www.w3.org/1999/xlink');
|
|
435
|
-
}
|
|
436
|
-
if (fixNamedEntities) {
|
|
437
|
-
for (const key of Object.keys(namedEntities)) {
|
|
438
|
-
const regexp = new RegExp(key, 'gi');
|
|
439
|
-
if (regexp.test(serialized)) {
|
|
440
|
-
serialized = serialized.replace(regexp, namedEntities[key]);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
return serialized;
|
|
445
|
-
}
|
|
446
239
|
getSvg() {
|
|
447
240
|
return this.__root;
|
|
448
241
|
}
|
|
@@ -452,8 +245,7 @@ export class Context {
|
|
|
452
245
|
this.__groupStack.push(parent);
|
|
453
246
|
parent.appendChild(group);
|
|
454
247
|
this.__currentElement = group;
|
|
455
|
-
|
|
456
|
-
this.__styleStack.push(style);
|
|
248
|
+
this.__styleStack.push(this.__getStyleState());
|
|
457
249
|
if (!this.__transformMatrixStack) {
|
|
458
250
|
this.__transformMatrixStack = [];
|
|
459
251
|
}
|
|
@@ -461,12 +253,10 @@ export class Context {
|
|
|
461
253
|
}
|
|
462
254
|
restore() {
|
|
463
255
|
this.__currentElement = this.__groupStack.pop();
|
|
464
|
-
this.__currentElementsToStyle = undefined;
|
|
465
256
|
if (!this.__currentElement) {
|
|
466
|
-
this.__currentElement = this.__root.childNodes[
|
|
257
|
+
this.__currentElement = this.__root.childNodes[0];
|
|
467
258
|
}
|
|
468
|
-
|
|
469
|
-
this.__applyStyleState(state);
|
|
259
|
+
this.__applyStyleState(this.__styleStack.pop());
|
|
470
260
|
if (this.__transformMatrixStack && this.__transformMatrixStack.length > 0) {
|
|
471
261
|
this.setTransform(this.__transformMatrixStack.pop());
|
|
472
262
|
}
|
|
@@ -475,14 +265,12 @@ export class Context {
|
|
|
475
265
|
this.__currentDefaultPath = '';
|
|
476
266
|
this.__currentPosition = {};
|
|
477
267
|
const path = this.__createElement('path', {}, true);
|
|
478
|
-
|
|
479
|
-
parent.appendChild(path);
|
|
268
|
+
this.__closestGroupOrSvg().appendChild(path);
|
|
480
269
|
this.__currentElement = path;
|
|
481
270
|
}
|
|
482
271
|
__applyCurrentDefaultPath() {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
currentElement.setAttribute('d', this.__currentDefaultPath);
|
|
272
|
+
if (this.__currentElement.nodeName === 'path') {
|
|
273
|
+
this.__currentElement.setAttribute('d', this.__currentDefaultPath);
|
|
486
274
|
}
|
|
487
275
|
}
|
|
488
276
|
__addPathCommand(command) {
|
|
@@ -494,10 +282,8 @@ export class Context {
|
|
|
494
282
|
this.beginPath();
|
|
495
283
|
}
|
|
496
284
|
this.__currentPosition = { x, y };
|
|
497
|
-
this.
|
|
498
|
-
|
|
499
|
-
y: this.__matrixTransform(x, y).y,
|
|
500
|
-
}));
|
|
285
|
+
const p = this.__matrixTransform(x, y);
|
|
286
|
+
this.__addPathCommand(`M ${p.x} ${p.y}`);
|
|
501
287
|
}
|
|
502
288
|
closePath() {
|
|
503
289
|
if (this.__currentDefaultPath) {
|
|
@@ -506,171 +292,106 @@ export class Context {
|
|
|
506
292
|
}
|
|
507
293
|
lineTo(x, y) {
|
|
508
294
|
this.__currentPosition = { x, y };
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}));
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
this.__addPathCommand(format('M {x} {y}', {
|
|
517
|
-
x: this.__matrixTransform(x, y).x,
|
|
518
|
-
y: this.__matrixTransform(x, y).y,
|
|
519
|
-
}));
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
523
|
-
this.__currentPosition = { x, y };
|
|
524
|
-
this.__addPathCommand(format('C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}', {
|
|
525
|
-
cp1x: this.__matrixTransform(cp1x, cp1y).x,
|
|
526
|
-
cp1y: this.__matrixTransform(cp1x, cp1y).y,
|
|
527
|
-
cp2x: this.__matrixTransform(cp2x, cp2y).x,
|
|
528
|
-
cp2y: this.__matrixTransform(cp2x, cp2y).y,
|
|
529
|
-
x: this.__matrixTransform(x, y).x,
|
|
530
|
-
y: this.__matrixTransform(x, y).y,
|
|
531
|
-
}));
|
|
295
|
+
const p = this.__matrixTransform(x, y);
|
|
296
|
+
this.__addPathCommand(this.__currentDefaultPath.includes('M')
|
|
297
|
+
? `L ${p.x} ${p.y}`
|
|
298
|
+
: `M ${p.x} ${p.y}`);
|
|
532
299
|
}
|
|
533
300
|
quadraticCurveTo(cpx, cpy, x, y) {
|
|
534
301
|
this.__currentPosition = { x, y };
|
|
535
|
-
this.
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
x: this.__matrixTransform(x, y).x,
|
|
539
|
-
y: this.__matrixTransform(x, y).y,
|
|
540
|
-
}));
|
|
302
|
+
const cp = this.__matrixTransform(cpx, cpy);
|
|
303
|
+
const p = this.__matrixTransform(x, y);
|
|
304
|
+
this.__addPathCommand(`Q ${cp.x} ${cp.y} ${p.x} ${p.y}`);
|
|
541
305
|
}
|
|
542
|
-
stroke
|
|
543
|
-
|
|
544
|
-
|
|
306
|
+
// paint-order matters only on an element with both fill and stroke: the
|
|
307
|
+
// collapsed-clade triangle
|
|
308
|
+
__applyPaintOrder(order, other) {
|
|
309
|
+
const current = this.__currentElement;
|
|
310
|
+
if (current.nodeName === 'path' &&
|
|
311
|
+
current.hasAttribute(other) &&
|
|
312
|
+
current.getAttribute(other) !== 'none') {
|
|
313
|
+
current.setAttribute('paint-order', order);
|
|
545
314
|
}
|
|
315
|
+
}
|
|
316
|
+
stroke() {
|
|
317
|
+
this.__applyPaintOrder('fill stroke markers', 'fill');
|
|
546
318
|
this.__applyCurrentDefaultPath();
|
|
547
319
|
this.__applyStyleToCurrentElement('stroke');
|
|
548
320
|
}
|
|
549
321
|
fill() {
|
|
550
|
-
|
|
551
|
-
this.__currentElement.setAttribute('paint-order', 'stroke fill markers');
|
|
552
|
-
}
|
|
322
|
+
this.__applyPaintOrder('stroke fill markers', 'stroke');
|
|
553
323
|
this.__applyCurrentDefaultPath();
|
|
554
324
|
this.__applyStyleToCurrentElement('fill');
|
|
555
325
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
this.beginPath();
|
|
559
|
-
}
|
|
560
|
-
this.moveTo(x, y);
|
|
561
|
-
this.lineTo(x + width, y);
|
|
562
|
-
this.lineTo(x + width, y + height);
|
|
563
|
-
this.lineTo(x, y + height);
|
|
564
|
-
this.lineTo(x, y);
|
|
565
|
-
this.closePath();
|
|
566
|
-
}
|
|
567
|
-
__clearCanvas() {
|
|
568
|
-
const rootGroup = this.__root.childNodes[1];
|
|
569
|
-
this.__root.removeChild(rootGroup);
|
|
570
|
-
this.__currentElement = this.__document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
571
|
-
this.__root.appendChild(this.__currentElement);
|
|
572
|
-
this.__groupStack = [];
|
|
573
|
-
}
|
|
326
|
+
// A fill covering the whole context paints over what is under it, as on a
|
|
327
|
+
// canvas; it does not clear the drawing.
|
|
574
328
|
fillRect(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 &&
|
|
578
|
-
y === 0 &&
|
|
579
|
-
width === this.width &&
|
|
580
|
-
height === this.height) {
|
|
581
|
-
this.__clearCanvas();
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
329
|
const rect = this.__createElement('rect', { x, y, width, height }, true);
|
|
585
|
-
|
|
586
|
-
parent.appendChild(rect);
|
|
330
|
+
this.__closestGroupOrSvg().appendChild(rect);
|
|
587
331
|
this.__currentElement = rect;
|
|
588
332
|
this.__applyTransformation(rect);
|
|
589
333
|
this.__applyStyleToCurrentElement('fill');
|
|
590
334
|
}
|
|
591
335
|
strokeRect(x, y, width, height) {
|
|
592
336
|
const rect = this.__createElement('rect', { x, y, width, height }, true);
|
|
593
|
-
|
|
594
|
-
parent.appendChild(rect);
|
|
337
|
+
this.__closestGroupOrSvg().appendChild(rect);
|
|
595
338
|
this.__currentElement = rect;
|
|
596
339
|
this.__applyTransformation(rect);
|
|
597
340
|
this.__applyStyleToCurrentElement('stroke');
|
|
598
341
|
}
|
|
599
342
|
clearRect(x, y, width, height) {
|
|
600
343
|
const { a, b, c, d, e, f } = this.getTransform();
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
344
|
+
if (a === 1 &&
|
|
345
|
+
b === 0 &&
|
|
346
|
+
c === 0 &&
|
|
347
|
+
d === 1 &&
|
|
348
|
+
e === 0 &&
|
|
349
|
+
f === 0 &&
|
|
350
|
+
x === 0 &&
|
|
351
|
+
y === 0 &&
|
|
352
|
+
width === this.width &&
|
|
353
|
+
height === this.height) {
|
|
354
|
+
this.__clearCanvas();
|
|
355
|
+
return;
|
|
609
356
|
}
|
|
610
357
|
const rect = this.__createElement('rect', { x, y, width, height, fill: '#FFFFFF' }, true);
|
|
611
358
|
this.__applyTransformation(rect);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return new CanvasGradient(grad, this);
|
|
626
|
-
}
|
|
627
|
-
createRadialGradient(x0, y0, r0, x1, y1, r1) {
|
|
628
|
-
const grad = this.__createElement('radialGradient', {
|
|
629
|
-
id: randomString(this.__ids),
|
|
630
|
-
cx: x1 + 'px',
|
|
631
|
-
cy: y1 + 'px',
|
|
632
|
-
r: r1 + 'px',
|
|
633
|
-
fx: x0 + 'px',
|
|
634
|
-
fy: y0 + 'px',
|
|
635
|
-
gradientUnits: 'userSpaceOnUse',
|
|
636
|
-
});
|
|
637
|
-
this.__defs.appendChild(grad);
|
|
638
|
-
return new CanvasGradient(grad, this);
|
|
639
|
-
}
|
|
640
|
-
__applyText(text, x, y, action) {
|
|
359
|
+
this.__closestGroupOrSvg().appendChild(rect);
|
|
360
|
+
}
|
|
361
|
+
__clearCanvas() {
|
|
362
|
+
this.__root.removeChild(this.__root.childNodes[0]);
|
|
363
|
+
this.__currentElement = this.__document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
364
|
+
this.__root.appendChild(this.__currentElement);
|
|
365
|
+
this.__groupStack = [];
|
|
366
|
+
}
|
|
367
|
+
__parsedFont() {
|
|
368
|
+
const hit = this.__fontCache.get(this.font);
|
|
369
|
+
if (hit) {
|
|
370
|
+
return hit;
|
|
371
|
+
}
|
|
641
372
|
const el = document.createElement('span');
|
|
642
|
-
el.setAttribute('style',
|
|
643
|
-
|
|
373
|
+
el.setAttribute('style', `font:${this.font}`);
|
|
374
|
+
this.__fontCache.set(this.font, el.style);
|
|
375
|
+
return el.style;
|
|
376
|
+
}
|
|
377
|
+
fillText(text, x, y) {
|
|
378
|
+
const style = this.__parsedFont();
|
|
644
379
|
const parent = this.__closestGroupOrSvg();
|
|
645
380
|
const textElement = this.__createElement('text', {
|
|
646
381
|
'font-family': style.fontFamily,
|
|
647
382
|
'font-size': style.fontSize,
|
|
648
383
|
'font-style': omitDefault('font-style', style.fontStyle),
|
|
649
384
|
'font-weight': omitDefault('font-weight', style.fontWeight),
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
y: y,
|
|
385
|
+
x,
|
|
386
|
+
y,
|
|
653
387
|
'text-anchor': getTextAnchor(this.textAlign),
|
|
654
388
|
'dominant-baseline': omitDefault('dominant-baseline', getDominantBaseline(this.textBaseline)),
|
|
655
389
|
}, true);
|
|
656
390
|
textElement.appendChild(this.__document.createTextNode(text));
|
|
657
391
|
this.__currentElement = textElement;
|
|
658
392
|
this.__applyTransformation(textElement);
|
|
659
|
-
this.__applyStyleToCurrentElement(
|
|
660
|
-
|
|
661
|
-
if (this.__fontHref) {
|
|
662
|
-
const a = this.__createElement('a');
|
|
663
|
-
a.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', this.__fontHref);
|
|
664
|
-
a.appendChild(textElement);
|
|
665
|
-
finalElement = a;
|
|
666
|
-
}
|
|
667
|
-
parent.appendChild(finalElement);
|
|
668
|
-
}
|
|
669
|
-
fillText(text, x, y) {
|
|
670
|
-
this.__applyText(text, x, y, 'fill');
|
|
671
|
-
}
|
|
672
|
-
strokeText(text, x, y) {
|
|
673
|
-
this.__applyText(text, x, y, 'stroke');
|
|
393
|
+
this.__applyStyleToCurrentElement('fill');
|
|
394
|
+
parent.appendChild(textElement);
|
|
674
395
|
}
|
|
675
396
|
measureText(text) {
|
|
676
397
|
this.__ctx.font = this.font;
|
|
@@ -692,60 +413,31 @@ export class Context {
|
|
|
692
413
|
const startX = x + radius * Math.cos(startAngle);
|
|
693
414
|
const startY = y + radius * Math.sin(startAngle);
|
|
694
415
|
const sweepFlag = counterClockwise ? 0 : 1;
|
|
695
|
-
let largeArcFlag = 0;
|
|
696
416
|
let diff = endAngle - startAngle;
|
|
697
417
|
if (diff < 0) {
|
|
698
418
|
diff += 2 * Math.PI;
|
|
699
419
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const scaleY =
|
|
420
|
+
const largeArcFlag = counterClockwise
|
|
421
|
+
? diff > Math.PI
|
|
422
|
+
? 0
|
|
423
|
+
: 1
|
|
424
|
+
: diff > Math.PI
|
|
425
|
+
? 1
|
|
426
|
+
: 0;
|
|
427
|
+
const { x: scaleX, y: scaleY } = this.__getTransformScale();
|
|
428
|
+
const end = this.__matrixTransform(endX, endY);
|
|
708
429
|
this.lineTo(startX, startY);
|
|
709
|
-
this.__addPathCommand(
|
|
710
|
-
rx: radius * scaleX,
|
|
711
|
-
ry: radius * scaleY,
|
|
712
|
-
xAxisRotation: 0,
|
|
713
|
-
largeArcFlag,
|
|
714
|
-
sweepFlag,
|
|
715
|
-
endX: this.__matrixTransform(endX, endY).x,
|
|
716
|
-
endY: this.__matrixTransform(endX, endY).y,
|
|
717
|
-
}));
|
|
430
|
+
this.__addPathCommand(`A ${radius * scaleX} ${radius * scaleY} 0 ${largeArcFlag} ${sweepFlag} ${end.x} ${end.y}`);
|
|
718
431
|
this.__currentPosition = { x: endX, y: endY };
|
|
719
432
|
}
|
|
720
|
-
clip() {
|
|
721
|
-
const group = this.__closestGroupOrSvg();
|
|
722
|
-
const clipPath = this.__createElement('clipPath');
|
|
723
|
-
const id = randomString(this.__ids);
|
|
724
|
-
const newGroup = this.__createElement('g');
|
|
725
|
-
this.__applyCurrentDefaultPath();
|
|
726
|
-
group.removeChild(this.__currentElement);
|
|
727
|
-
clipPath.setAttribute('id', id);
|
|
728
|
-
clipPath.appendChild(this.__currentElement);
|
|
729
|
-
this.__defs.appendChild(clipPath);
|
|
730
|
-
group.setAttribute('clip-path', format('url(#{id})', { id }));
|
|
731
|
-
group.appendChild(newGroup);
|
|
732
|
-
this.__currentElement = newGroup;
|
|
733
|
-
}
|
|
734
433
|
setLineDash(dashArray) {
|
|
735
|
-
|
|
736
|
-
this.lineDash = dashArray.join(',');
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
this.lineDash = null;
|
|
740
|
-
}
|
|
434
|
+
this.lineDash = dashArray.length > 0 ? dashArray.join(',') : null;
|
|
741
435
|
}
|
|
742
436
|
setTransform(a, b, c, d, e, f) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
this.__transformMatrix = new DOMMatrix([a, b, c, d, e, f]);
|
|
748
|
-
}
|
|
437
|
+
this.__transformMatrix =
|
|
438
|
+
a instanceof DOMMatrix
|
|
439
|
+
? new DOMMatrix([a.a, a.b, a.c, a.d, a.e, a.f])
|
|
440
|
+
: new DOMMatrix([a, b, c, d, e, f]);
|
|
749
441
|
}
|
|
750
442
|
getTransform() {
|
|
751
443
|
const { a, b, c, d, e, f } = this.__transformMatrix;
|
|
@@ -761,27 +453,10 @@ export class Context {
|
|
|
761
453
|
if (isNaN(x) || isNaN(y) || !isFinite(x) || !isFinite(y)) {
|
|
762
454
|
return;
|
|
763
455
|
}
|
|
764
|
-
|
|
765
|
-
this.setTransform(matrix);
|
|
766
|
-
}
|
|
767
|
-
rotate(angle) {
|
|
768
|
-
const matrix = this.getTransform().multiply(new DOMMatrix([
|
|
769
|
-
Math.cos(angle),
|
|
770
|
-
Math.sin(angle),
|
|
771
|
-
-Math.sin(angle),
|
|
772
|
-
Math.cos(angle),
|
|
773
|
-
0,
|
|
774
|
-
0,
|
|
775
|
-
]));
|
|
776
|
-
this.setTransform(matrix);
|
|
456
|
+
this.setTransform(this.getTransform().scale(x, y));
|
|
777
457
|
}
|
|
778
458
|
translate(x, y) {
|
|
779
|
-
|
|
780
|
-
this.setTransform(matrix);
|
|
781
|
-
}
|
|
782
|
-
transform(a, b, c, d, e, f) {
|
|
783
|
-
const matrix = this.getTransform().multiply(new DOMMatrix([a, b, c, d, e, f]));
|
|
784
|
-
this.setTransform(matrix);
|
|
459
|
+
this.setTransform(this.getTransform().translate(x, y));
|
|
785
460
|
}
|
|
786
461
|
__matrixTransform(x, y) {
|
|
787
462
|
return new DOMPoint(x, y).matrixTransform(this.__transformMatrix);
|
|
@@ -792,62 +467,5 @@ export class Context {
|
|
|
792
467
|
y: Math.hypot(this.__transformMatrix.c, this.__transformMatrix.d),
|
|
793
468
|
};
|
|
794
469
|
}
|
|
795
|
-
__getTransformRotation() {
|
|
796
|
-
return Math.atan2(this.__transformMatrix.b, this.__transformMatrix.a);
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* An <image> of the source, cropped and scaled the way the canvas call asks:
|
|
800
|
-
* a nested <svg> whose viewBox is the source rectangle maps it onto the
|
|
801
|
-
* destination rectangle exactly. Throws rather than dropping the image, so a
|
|
802
|
-
* renderer that draws one into an export finds out.
|
|
803
|
-
*/
|
|
804
|
-
drawImage(image, ...args) {
|
|
805
|
-
const source = image;
|
|
806
|
-
if (typeof source.toDataURL !== 'function') {
|
|
807
|
-
throw new TypeError('svgcanvas drawImage needs a source with toDataURL (a canvas); an OffscreenCanvas has to be copied onto one first');
|
|
808
|
-
}
|
|
809
|
-
const [sx, sy, sw, sh, dx, dy, dw, dh] = args.length >= 8
|
|
810
|
-
? args
|
|
811
|
-
: [
|
|
812
|
-
0,
|
|
813
|
-
0,
|
|
814
|
-
source.width,
|
|
815
|
-
source.height,
|
|
816
|
-
args[0],
|
|
817
|
-
args[1],
|
|
818
|
-
args[2] ?? source.width,
|
|
819
|
-
args[3] ?? source.height,
|
|
820
|
-
];
|
|
821
|
-
const group = this.__createElement('g');
|
|
822
|
-
const viewport = this.__createElement('svg', {
|
|
823
|
-
x: dx,
|
|
824
|
-
y: dy,
|
|
825
|
-
width: dw,
|
|
826
|
-
height: dh,
|
|
827
|
-
viewBox: `${sx} ${sy} ${sw} ${sh}`,
|
|
828
|
-
preserveAspectRatio: 'none',
|
|
829
|
-
});
|
|
830
|
-
const img = this.__createElement('image', {
|
|
831
|
-
href: source.toDataURL('image/png'),
|
|
832
|
-
width: source.width,
|
|
833
|
-
height: source.height,
|
|
834
|
-
preserveAspectRatio: 'none',
|
|
835
|
-
'image-rendering': this.imageSmoothingEnabled ? undefined : 'pixelated',
|
|
836
|
-
});
|
|
837
|
-
viewport.appendChild(img);
|
|
838
|
-
group.appendChild(viewport);
|
|
839
|
-
this.__closestGroupOrSvg().appendChild(group);
|
|
840
|
-
this.__currentElement = group;
|
|
841
|
-
this.__applyTransformation(group);
|
|
842
|
-
}
|
|
843
|
-
imageSmoothingEnabled = true;
|
|
844
|
-
// Stubs for unimplemented methods
|
|
845
|
-
drawFocusRing() { }
|
|
846
|
-
createImageData() { }
|
|
847
|
-
putImageData() { }
|
|
848
|
-
globalCompositeOperation() { }
|
|
849
|
-
createPattern() {
|
|
850
|
-
return null;
|
|
851
|
-
}
|
|
852
470
|
}
|
|
853
471
|
//# sourceMappingURL=index.js.map
|