@dicebear/core 10.0.1 → 10.1.0-rc.1
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/lib/Options.js +4 -1
- package/lib/Renderer.js +23 -19
- package/lib/Utils/Number.d.ts +12 -0
- package/lib/Utils/Number.js +32 -0
- package/package.json +1 -1
package/lib/Options.js
CHANGED
|
@@ -123,5 +123,8 @@ _Options_data = new WeakMap(), _Options_instances = new WeakSet(), _Options_dyna
|
|
|
123
123
|
if (typeof value === 'number') {
|
|
124
124
|
return { min: value, max: value };
|
|
125
125
|
}
|
|
126
|
-
|
|
126
|
+
if (value.length === 0) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
return { min: Math.min(...value), max: Math.max(...value) };
|
|
127
130
|
};
|
package/lib/Renderer.js
CHANGED
|
@@ -13,6 +13,7 @@ var _Renderer_instances, _Renderer_style, _Renderer_resolver, _Renderer_defs, _R
|
|
|
13
13
|
import { Fnv1a } from './Prng/Fnv1a.js';
|
|
14
14
|
import { Initials } from './Utils/Initials.js';
|
|
15
15
|
import { License } from './Utils/License.js';
|
|
16
|
+
import { Number } from './Utils/Number.js';
|
|
16
17
|
import { Xml } from './Utils/Xml.js';
|
|
17
18
|
/**
|
|
18
19
|
* Walks a style's element tree and turns it into the final SVG markup.
|
|
@@ -54,7 +55,7 @@ export class Renderer {
|
|
|
54
55
|
const escapedTitle = title !== undefined ? Xml.escape(title) : undefined;
|
|
55
56
|
const attrs = [
|
|
56
57
|
'xmlns="http://www.w3.org/2000/svg"',
|
|
57
|
-
`viewBox="0 0 ${canvas.width()} ${canvas.height()}"`,
|
|
58
|
+
`viewBox="0 0 ${Number.format(canvas.width())} ${Number.format(canvas.height())}"`,
|
|
58
59
|
];
|
|
59
60
|
const rootAttributes = __classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_renderAttributes).call(this, __classPrivateFieldGet(this, _Renderer_style, "f").attributes());
|
|
60
61
|
if (rootAttributes) {
|
|
@@ -67,7 +68,8 @@ export class Renderer {
|
|
|
67
68
|
attrs.push('aria-hidden="true"');
|
|
68
69
|
}
|
|
69
70
|
if (size !== undefined) {
|
|
70
|
-
|
|
71
|
+
const sizeValue = Number.format(size);
|
|
72
|
+
attrs.push(`width="${sizeValue}"`, `height="${sizeValue}"`);
|
|
71
73
|
}
|
|
72
74
|
const titleElement = escapedTitle !== undefined ? `<title>${escapedTitle}</title>` : '';
|
|
73
75
|
let svg = `<svg ${attrs.join(' ')}>${metadata}${defs}${titleElement}${body}</svg>`;
|
|
@@ -82,8 +84,8 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
|
|
|
82
84
|
if (flip === 'none') {
|
|
83
85
|
return body;
|
|
84
86
|
}
|
|
85
|
-
const w = canvas.width();
|
|
86
|
-
const h = canvas.height();
|
|
87
|
+
const w = Number.format(canvas.width());
|
|
88
|
+
const h = Number.format(canvas.height());
|
|
87
89
|
let transform;
|
|
88
90
|
switch (flip) {
|
|
89
91
|
case 'horizontal':
|
|
@@ -104,13 +106,13 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
|
|
|
104
106
|
}
|
|
105
107
|
const cx = canvas.width() / 2;
|
|
106
108
|
const cy = canvas.height() / 2;
|
|
107
|
-
return `<g transform="translate(${cx}, ${cy}) scale(${scale}) translate(${-cx}, ${-cy})">${body}</g>`;
|
|
109
|
+
return `<g transform="translate(${Number.format(cx)}, ${Number.format(cy)}) scale(${Number.format(scale)}) translate(${Number.format(-cx)}, ${Number.format(-cy)})">${body}</g>`;
|
|
108
110
|
}, _Renderer_applyBorderRadius = function _Renderer_applyBorderRadius(body, canvas) {
|
|
109
111
|
const radius = __classPrivateFieldGet(this, _Renderer_resolver, "f").borderRadius();
|
|
110
112
|
const id = `clip-${__classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_hashSeed).call(this)}`;
|
|
111
|
-
const rx = (radius / 100) * canvas.width();
|
|
112
|
-
const ry = (radius / 100) * canvas.height();
|
|
113
|
-
__classPrivateFieldGet(this, _Renderer_defs, "f").set(id, `<clipPath id="${id}"><rect width="${canvas.width()}" height="${canvas.height()}" rx="${rx}" ry="${ry}"/></clipPath>`);
|
|
113
|
+
const rx = Number.format((radius / 100) * canvas.width());
|
|
114
|
+
const ry = Number.format((radius / 100) * canvas.height());
|
|
115
|
+
__classPrivateFieldGet(this, _Renderer_defs, "f").set(id, `<clipPath id="${id}"><rect width="${Number.format(canvas.width())}" height="${Number.format(canvas.height())}" rx="${rx}" ry="${ry}"/></clipPath>`);
|
|
114
116
|
return `<g clip-path="url(#${id})">${body}</g>`;
|
|
115
117
|
}, _Renderer_applyRotate = function _Renderer_applyRotate(body, canvas) {
|
|
116
118
|
const rotate = __classPrivateFieldGet(this, _Renderer_resolver, "f").rotate();
|
|
@@ -119,22 +121,22 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
|
|
|
119
121
|
}
|
|
120
122
|
const cx = canvas.width() / 2;
|
|
121
123
|
const cy = canvas.height() / 2;
|
|
122
|
-
return `<g transform="rotate(${rotate}, ${cx}, ${cy})">${body}</g>`;
|
|
124
|
+
return `<g transform="rotate(${Number.format(rotate)}, ${Number.format(cx)}, ${Number.format(cy)})">${body}</g>`;
|
|
123
125
|
}, _Renderer_applyTranslate = function _Renderer_applyTranslate(body, canvas) {
|
|
124
126
|
const tx = __classPrivateFieldGet(this, _Renderer_resolver, "f").translateX();
|
|
125
127
|
const ty = __classPrivateFieldGet(this, _Renderer_resolver, "f").translateY();
|
|
126
128
|
if (tx === 0 && ty === 0) {
|
|
127
129
|
return body;
|
|
128
130
|
}
|
|
129
|
-
const x = (tx / 100) * canvas.width();
|
|
130
|
-
const y = (ty / 100) * canvas.height();
|
|
131
|
+
const x = Number.format((tx / 100) * canvas.width());
|
|
132
|
+
const y = Number.format((ty / 100) * canvas.height());
|
|
131
133
|
return `<g transform="translate(${x}, ${y})">${body}</g>`;
|
|
132
134
|
}, _Renderer_renderBackground = function _Renderer_renderBackground(canvas) {
|
|
133
135
|
const colors = __classPrivateFieldGet(this, _Renderer_resolver, "f").color('background');
|
|
134
136
|
if (colors.length === 0) {
|
|
135
137
|
return '';
|
|
136
138
|
}
|
|
137
|
-
return `<rect width="${canvas.width()}" height="${canvas.height()}" fill="${Xml.escape(__classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_resolveColorReference).call(this, 'background'))}"/>`;
|
|
139
|
+
return `<rect width="${Number.format(canvas.width())}" height="${Number.format(canvas.height())}" fill="${Xml.escape(__classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_resolveColorReference).call(this, 'background'))}"/>`;
|
|
138
140
|
}, _Renderer_randomizeIds = function _Renderer_randomizeIds(svg) {
|
|
139
141
|
const suffix = Math.floor(Math.random() * 0xffffff)
|
|
140
142
|
.toString(16)
|
|
@@ -227,16 +229,18 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
|
|
|
227
229
|
const transforms = [];
|
|
228
230
|
const cx = component.width() / 2;
|
|
229
231
|
const cy = component.height() / 2;
|
|
232
|
+
const cxValue = Number.format(cx);
|
|
233
|
+
const cyValue = Number.format(cy);
|
|
230
234
|
if (translateX !== 0 || translateY !== 0) {
|
|
231
|
-
const x =
|
|
232
|
-
const y =
|
|
235
|
+
const x = Number.format((translateX / 100) * component.width());
|
|
236
|
+
const y = Number.format((translateY / 100) * component.height());
|
|
233
237
|
transforms.push(`translate(${x}, ${y})`);
|
|
234
238
|
}
|
|
235
239
|
if (rotate !== 0) {
|
|
236
|
-
transforms.push(`rotate(${rotate}, ${
|
|
240
|
+
transforms.push(`rotate(${Number.format(rotate)}, ${cxValue}, ${cyValue})`);
|
|
237
241
|
}
|
|
238
242
|
if (scale !== 1) {
|
|
239
|
-
transforms.push(`translate(${
|
|
243
|
+
transforms.push(`translate(${cxValue}, ${cyValue}) scale(${Number.format(scale)}) translate(${Number.format(-cx)}, ${Number.format(-cy)})`);
|
|
240
244
|
}
|
|
241
245
|
return transforms;
|
|
242
246
|
}, _Renderer_renderAttributes = function _Renderer_renderAttributes(attributes) {
|
|
@@ -274,10 +278,10 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
|
|
|
274
278
|
const id = `${name}-color-${__classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_hashSeed).call(this)}`;
|
|
275
279
|
const tag = fill === 'linear' ? 'linearGradient' : 'radialGradient';
|
|
276
280
|
const rotateAttr = rotation !== 0
|
|
277
|
-
? ` gradientTransform="rotate(${rotation}, 0.5, 0.5)"`
|
|
281
|
+
? ` gradientTransform="rotate(${Number.format(rotation)}, 0.5, 0.5)"`
|
|
278
282
|
: '';
|
|
279
283
|
const stops = colors.map((color, i) => {
|
|
280
|
-
const offset =
|
|
284
|
+
const offset = Number.format((i / (colors.length - 1)) * 100);
|
|
281
285
|
return `<stop offset="${offset}%" stop-color="${Xml.escape(color)}"/>`;
|
|
282
286
|
});
|
|
283
287
|
__classPrivateFieldGet(this, _Renderer_defs, "f").set(id, `<${tag} id="${id}"${rotateAttr}>${stops.join('')}</${tag}>`);
|
|
@@ -297,7 +301,7 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
|
|
|
297
301
|
case 'initials':
|
|
298
302
|
return __classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_initials).call(this);
|
|
299
303
|
case 'fontWeight':
|
|
300
|
-
return
|
|
304
|
+
return Number.format(__classPrivateFieldGet(this, _Renderer_resolver, "f").fontWeight());
|
|
301
305
|
case 'fontFamily':
|
|
302
306
|
return __classPrivateFieldGet(this, _Renderer_resolver, "f").fontFamily();
|
|
303
307
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a number for SVG output, rounded to at most 5 decimal places.
|
|
3
|
+
*
|
|
4
|
+
* Rounding to a fixed precision keeps the output bounded and identical across
|
|
5
|
+
* the JS, PHP, and Python ports: every value becomes a multiple of 1e-5 in the
|
|
6
|
+
* SVG coordinate range, which has no exponential form, so the result is built
|
|
7
|
+
* from integer arithmetic (no locale- or language-specific float stringifying).
|
|
8
|
+
* Five decimals is far below sub-pixel precision for any realistic canvas.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Number {
|
|
11
|
+
static format(value: number): string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a number for SVG output, rounded to at most 5 decimal places.
|
|
3
|
+
*
|
|
4
|
+
* Rounding to a fixed precision keeps the output bounded and identical across
|
|
5
|
+
* the JS, PHP, and Python ports: every value becomes a multiple of 1e-5 in the
|
|
6
|
+
* SVG coordinate range, which has no exponential form, so the result is built
|
|
7
|
+
* from integer arithmetic (no locale- or language-specific float stringifying).
|
|
8
|
+
* Five decimals is far below sub-pixel precision for any realistic canvas.
|
|
9
|
+
*/
|
|
10
|
+
export class Number {
|
|
11
|
+
static format(value) {
|
|
12
|
+
// `value !== value` and the Infinity literals avoid referencing the global
|
|
13
|
+
// `Number`, which this class shadows within the module.
|
|
14
|
+
if (value !== value) {
|
|
15
|
+
return 'NaN';
|
|
16
|
+
}
|
|
17
|
+
if (value === Infinity) {
|
|
18
|
+
return 'Infinity';
|
|
19
|
+
}
|
|
20
|
+
if (value === -Infinity) {
|
|
21
|
+
return '-Infinity';
|
|
22
|
+
}
|
|
23
|
+
let scaled = Math.round(value * 100000);
|
|
24
|
+
const sign = scaled < 0 ? '-' : '';
|
|
25
|
+
scaled = Math.abs(scaled);
|
|
26
|
+
const integerPart = Math.floor(scaled / 100000);
|
|
27
|
+
const fraction = String(scaled % 100000)
|
|
28
|
+
.padStart(5, '0')
|
|
29
|
+
.replace(/0+$/, '');
|
|
30
|
+
return `${sign}${integerPart}${fraction ? `.${fraction}` : ''}`;
|
|
31
|
+
}
|
|
32
|
+
}
|