@effing/canvas 0.18.0 → 0.18.2
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 +460 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createCanvas as _createCanvas } from "@napi-rs/canvas";
|
|
|
3
3
|
import {
|
|
4
4
|
Canvas,
|
|
5
5
|
GlobalFonts as GlobalFonts2,
|
|
6
|
-
loadImage as
|
|
6
|
+
loadImage as loadImage5,
|
|
7
7
|
Image
|
|
8
8
|
} from "@napi-rs/canvas";
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ function renderLottieFrame(ctx, animation, frame) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// src/jsx/draw/index.ts
|
|
24
|
-
import { loadImage as loadImage3 } from "@napi-rs/canvas";
|
|
24
|
+
import { createCanvas as createCanvas2, loadImage as loadImage3 } from "@napi-rs/canvas";
|
|
25
25
|
|
|
26
26
|
// src/jsx/language.ts
|
|
27
27
|
function isEmoji(char) {
|
|
@@ -617,8 +617,8 @@ function computeContain(imgW, imgH, boxX, boxY, boxW, boxH) {
|
|
|
617
617
|
}
|
|
618
618
|
|
|
619
619
|
// src/jsx/draw/image.ts
|
|
620
|
-
async function drawImage(ctx, src, x, y, width, height, style) {
|
|
621
|
-
const image = await loadImage(src);
|
|
620
|
+
async function drawImage(ctx, src, x, y, width, height, style, preloadedImage) {
|
|
621
|
+
const image = preloadedImage ?? await loadImage(src);
|
|
622
622
|
const objectFit = style?.objectFit ?? "fill";
|
|
623
623
|
if (objectFit === "fill") {
|
|
624
624
|
ctx.drawImage(image, x, y, width, height);
|
|
@@ -795,6 +795,11 @@ function toNumber(v) {
|
|
|
795
795
|
|
|
796
796
|
// src/jsx/draw/svg.ts
|
|
797
797
|
import { Path2D } from "@napi-rs/canvas";
|
|
798
|
+
function mergeStyleIntoProps(props) {
|
|
799
|
+
const style = props.style;
|
|
800
|
+
if (!style) return props;
|
|
801
|
+
return { ...props, ...style };
|
|
802
|
+
}
|
|
798
803
|
function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
799
804
|
ctx.save();
|
|
800
805
|
ctx.translate(x, y);
|
|
@@ -809,7 +814,8 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
809
814
|
ctx.translate(-vbX, -vbY);
|
|
810
815
|
}
|
|
811
816
|
}
|
|
812
|
-
const
|
|
817
|
+
const merged = mergeStyleIntoProps(node.props);
|
|
818
|
+
const inheritedFill = merged.fill ?? "black";
|
|
813
819
|
const children = node.props.children;
|
|
814
820
|
if (children != null) {
|
|
815
821
|
const childArray = Array.isArray(children) ? children : [children];
|
|
@@ -822,7 +828,8 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
822
828
|
ctx.restore();
|
|
823
829
|
}
|
|
824
830
|
function drawSvgChild(ctx, child, inheritedFill) {
|
|
825
|
-
const { type
|
|
831
|
+
const { type } = child;
|
|
832
|
+
const props = mergeStyleIntoProps(child.props);
|
|
826
833
|
switch (type) {
|
|
827
834
|
case "path":
|
|
828
835
|
drawPath(ctx, props, inheritedFill);
|
|
@@ -919,7 +926,8 @@ function drawPolyline(ctx, props, inheritedFill) {
|
|
|
919
926
|
function drawGroup(ctx, node, inheritedFill) {
|
|
920
927
|
const children = node.children ?? node.props.children;
|
|
921
928
|
if (children == null) return;
|
|
922
|
-
const
|
|
929
|
+
const merged = mergeStyleIntoProps(node.props);
|
|
930
|
+
const groupFill = merged.fill ?? inheritedFill;
|
|
923
931
|
const childArray = Array.isArray(children) ? children : [children];
|
|
924
932
|
for (const child of childArray) {
|
|
925
933
|
if (child != null && typeof child === "object") {
|
|
@@ -948,9 +956,9 @@ function applyStroke(ctx, props, path) {
|
|
|
948
956
|
const stroke = props.stroke;
|
|
949
957
|
if (!stroke || stroke === "none") return;
|
|
950
958
|
ctx.strokeStyle = stroke;
|
|
951
|
-
ctx.lineWidth = Number(props.strokeWidth ?? 1);
|
|
952
|
-
ctx.lineCap = props.strokeLinecap ?? "butt";
|
|
953
|
-
ctx.lineJoin = props.strokeLinejoin ?? "miter";
|
|
959
|
+
ctx.lineWidth = Number(props.strokeWidth ?? props["stroke-width"] ?? 1);
|
|
960
|
+
ctx.lineCap = props.strokeLinecap ?? props["stroke-linecap"] ?? "butt";
|
|
961
|
+
ctx.lineJoin = props.strokeLinejoin ?? props["stroke-linejoin"] ?? "miter";
|
|
954
962
|
ctx.stroke(path);
|
|
955
963
|
}
|
|
956
964
|
|
|
@@ -1168,6 +1176,34 @@ function drawTextDecoration(ctx, seg, offsetX, offsetY) {
|
|
|
1168
1176
|
}
|
|
1169
1177
|
|
|
1170
1178
|
// src/jsx/draw/index.ts
|
|
1179
|
+
var canvasPool = /* @__PURE__ */ new Map();
|
|
1180
|
+
function acquireOffscreen(w, h) {
|
|
1181
|
+
const key = `${w}x${h}`;
|
|
1182
|
+
const stack = canvasPool.get(key);
|
|
1183
|
+
if (stack) {
|
|
1184
|
+
while (stack.length > 0) {
|
|
1185
|
+
const ref = stack.pop();
|
|
1186
|
+
const canvas2 = ref.deref();
|
|
1187
|
+
if (canvas2) {
|
|
1188
|
+
const ctx = canvas2.getContext("2d");
|
|
1189
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1190
|
+
ctx.clearRect(0, 0, w, h);
|
|
1191
|
+
return [canvas2, ctx];
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
const canvas = createCanvas2(w, h);
|
|
1196
|
+
return [canvas, canvas.getContext("2d")];
|
|
1197
|
+
}
|
|
1198
|
+
function releaseOffscreen(canvas) {
|
|
1199
|
+
const key = `${canvas.width}x${canvas.height}`;
|
|
1200
|
+
let stack = canvasPool.get(key);
|
|
1201
|
+
if (!stack) {
|
|
1202
|
+
stack = [];
|
|
1203
|
+
canvasPool.set(key, stack);
|
|
1204
|
+
}
|
|
1205
|
+
stack.push(new WeakRef(canvas));
|
|
1206
|
+
}
|
|
1171
1207
|
async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
1172
1208
|
const x = parentX + node.x;
|
|
1173
1209
|
const y = parentY + node.y;
|
|
@@ -1175,6 +1211,61 @@ async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
|
1175
1211
|
if (style.display === "none") return;
|
|
1176
1212
|
const opacity = style.opacity ?? 1;
|
|
1177
1213
|
if (opacity <= 0) return;
|
|
1214
|
+
const scaleInfo = style.transform ? extractScale(style.transform) : null;
|
|
1215
|
+
if (scaleInfo && (scaleInfo.sx !== 1 || scaleInfo.sy !== 1)) {
|
|
1216
|
+
const sx = scaleInfo.sx;
|
|
1217
|
+
const sy = scaleInfo.sy;
|
|
1218
|
+
const transformWithoutScale = scaleInfo.remaining;
|
|
1219
|
+
const qx = Math.max(1, Math.ceil(Math.abs(sx)));
|
|
1220
|
+
const qy = Math.max(1, Math.ceil(Math.abs(sy)));
|
|
1221
|
+
const bufW = Math.ceil((width + 2) * qx);
|
|
1222
|
+
const bufH = Math.ceil((height + 2) * qy);
|
|
1223
|
+
if (bufW > 0 && bufH > 0) {
|
|
1224
|
+
const [offscreen, offCtx] = acquireOffscreen(bufW, bufH);
|
|
1225
|
+
offCtx.save();
|
|
1226
|
+
offCtx.scale(qx, qy);
|
|
1227
|
+
await drawNodeInner(
|
|
1228
|
+
offCtx,
|
|
1229
|
+
node,
|
|
1230
|
+
parentX,
|
|
1231
|
+
parentY,
|
|
1232
|
+
1 - x,
|
|
1233
|
+
1 - y,
|
|
1234
|
+
debug,
|
|
1235
|
+
emojiStyle,
|
|
1236
|
+
transformWithoutScale
|
|
1237
|
+
);
|
|
1238
|
+
offCtx.restore();
|
|
1239
|
+
ctx.save();
|
|
1240
|
+
if (opacity < 1) {
|
|
1241
|
+
ctx.globalAlpha *= opacity;
|
|
1242
|
+
}
|
|
1243
|
+
let ox = x + width / 2;
|
|
1244
|
+
let oy = y + height / 2;
|
|
1245
|
+
if (style.transformOrigin) {
|
|
1246
|
+
const parts = style.transformOrigin.split(/\s+/);
|
|
1247
|
+
ox = resolveOrigin(parts[0], x, width);
|
|
1248
|
+
oy = resolveOrigin(parts[1], y, height);
|
|
1249
|
+
}
|
|
1250
|
+
ctx.translate(ox, oy);
|
|
1251
|
+
ctx.scale(sx, sy);
|
|
1252
|
+
ctx.translate(-ox, -oy);
|
|
1253
|
+
ctx.drawImage(
|
|
1254
|
+
offscreen,
|
|
1255
|
+
0,
|
|
1256
|
+
0,
|
|
1257
|
+
bufW,
|
|
1258
|
+
bufH,
|
|
1259
|
+
x - 1,
|
|
1260
|
+
y - 1,
|
|
1261
|
+
width + 2,
|
|
1262
|
+
height + 2
|
|
1263
|
+
);
|
|
1264
|
+
releaseOffscreen(offscreen);
|
|
1265
|
+
ctx.restore();
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1178
1269
|
ctx.save();
|
|
1179
1270
|
if (opacity < 1) {
|
|
1180
1271
|
ctx.globalAlpha *= opacity;
|
|
@@ -1332,7 +1423,8 @@ async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
|
1332
1423
|
imgY,
|
|
1333
1424
|
imgW,
|
|
1334
1425
|
imgH,
|
|
1335
|
-
style
|
|
1426
|
+
style,
|
|
1427
|
+
node.props.__loadedImage
|
|
1336
1428
|
);
|
|
1337
1429
|
}
|
|
1338
1430
|
if (node.type === "svg") {
|
|
@@ -1344,6 +1436,194 @@ async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
|
1344
1436
|
}
|
|
1345
1437
|
ctx.restore();
|
|
1346
1438
|
}
|
|
1439
|
+
function extractScale(transform) {
|
|
1440
|
+
const scaleMatch = transform.match(/\b(scale|scaleX|scaleY)\(([^)]+)\)/);
|
|
1441
|
+
if (!scaleMatch) return null;
|
|
1442
|
+
const [fullMatch, name, args] = scaleMatch;
|
|
1443
|
+
const values = args.split(",").map((s) => s.trim());
|
|
1444
|
+
const sx = name === "scaleY" ? 1 : parseFloat(values[0]);
|
|
1445
|
+
const sy = name === "scaleX" ? 1 : parseFloat(values[name === "scale" ? 1 : 0] ?? String(sx));
|
|
1446
|
+
const remaining = transform.replace(fullMatch, "").trim();
|
|
1447
|
+
return { sx, sy, remaining };
|
|
1448
|
+
}
|
|
1449
|
+
async function drawNodeInner(ctx, node, parentX, parentY, offsetX, offsetY, debug, emojiStyle, overrideTransform) {
|
|
1450
|
+
const x = parentX + node.x + offsetX;
|
|
1451
|
+
const y = parentY + node.y + offsetY;
|
|
1452
|
+
const { width, height, style } = node;
|
|
1453
|
+
if (style.display === "none") return;
|
|
1454
|
+
const opacity = style.opacity ?? 1;
|
|
1455
|
+
if (opacity <= 0) return;
|
|
1456
|
+
ctx.save();
|
|
1457
|
+
if (opacity < 1) {
|
|
1458
|
+
ctx.globalAlpha *= opacity;
|
|
1459
|
+
}
|
|
1460
|
+
if (style.filter) {
|
|
1461
|
+
ctx.filter = style.filter;
|
|
1462
|
+
}
|
|
1463
|
+
const transformToApply = overrideTransform !== void 0 ? overrideTransform : style.transform;
|
|
1464
|
+
if (transformToApply) {
|
|
1465
|
+
applyTransform(
|
|
1466
|
+
ctx,
|
|
1467
|
+
transformToApply,
|
|
1468
|
+
x,
|
|
1469
|
+
y,
|
|
1470
|
+
width,
|
|
1471
|
+
height,
|
|
1472
|
+
style.transformOrigin
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
const isClipped = style.overflow === "hidden" || style.overflowX === "hidden" || style.overflowY === "hidden";
|
|
1476
|
+
if (isClipped) {
|
|
1477
|
+
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1478
|
+
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1479
|
+
}
|
|
1480
|
+
if (style.backgroundColor || style.borderTopWidth || style.borderRightWidth || style.borderBottomWidth || style.borderLeftWidth || style.boxShadow) {
|
|
1481
|
+
drawRect(ctx, x, y, width, height, style);
|
|
1482
|
+
}
|
|
1483
|
+
if (style.backgroundImage) {
|
|
1484
|
+
const gradient = createGradientFromCSS(
|
|
1485
|
+
ctx,
|
|
1486
|
+
style.backgroundImage,
|
|
1487
|
+
x,
|
|
1488
|
+
y,
|
|
1489
|
+
width,
|
|
1490
|
+
height
|
|
1491
|
+
);
|
|
1492
|
+
if (gradient) {
|
|
1493
|
+
ctx.fillStyle = gradient;
|
|
1494
|
+
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1495
|
+
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1496
|
+
ctx.beginPath();
|
|
1497
|
+
roundedRect(
|
|
1498
|
+
ctx,
|
|
1499
|
+
x,
|
|
1500
|
+
y,
|
|
1501
|
+
width,
|
|
1502
|
+
height,
|
|
1503
|
+
borderRadius.topLeft,
|
|
1504
|
+
borderRadius.topRight,
|
|
1505
|
+
borderRadius.bottomRight,
|
|
1506
|
+
borderRadius.bottomLeft
|
|
1507
|
+
);
|
|
1508
|
+
ctx.fill();
|
|
1509
|
+
} else {
|
|
1510
|
+
ctx.fillRect(x, y, width, height);
|
|
1511
|
+
}
|
|
1512
|
+
} else {
|
|
1513
|
+
const urlMatch = style.backgroundImage.match(/url\(["']?(.*?)["']?\)/);
|
|
1514
|
+
if (urlMatch) {
|
|
1515
|
+
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1516
|
+
const hasRadius2 = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
1517
|
+
if (hasRadius2) {
|
|
1518
|
+
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1519
|
+
}
|
|
1520
|
+
const image = await loadImage3(urlMatch[1]);
|
|
1521
|
+
const bgSize = style.backgroundSize;
|
|
1522
|
+
if (bgSize === "cover") {
|
|
1523
|
+
const r = computeCover(
|
|
1524
|
+
image.width,
|
|
1525
|
+
image.height,
|
|
1526
|
+
x,
|
|
1527
|
+
y,
|
|
1528
|
+
width,
|
|
1529
|
+
height
|
|
1530
|
+
);
|
|
1531
|
+
ctx.drawImage(image, r.sx, r.sy, r.sw, r.sh, r.dx, r.dy, r.dw, r.dh);
|
|
1532
|
+
} else {
|
|
1533
|
+
let tileW, tileH;
|
|
1534
|
+
if (bgSize === "contain") {
|
|
1535
|
+
const r = computeContain(
|
|
1536
|
+
image.width,
|
|
1537
|
+
image.height,
|
|
1538
|
+
0,
|
|
1539
|
+
0,
|
|
1540
|
+
width,
|
|
1541
|
+
height
|
|
1542
|
+
);
|
|
1543
|
+
tileW = r.dw;
|
|
1544
|
+
tileH = r.dh;
|
|
1545
|
+
} else if (bgSize === "100% 100%") {
|
|
1546
|
+
tileW = width;
|
|
1547
|
+
tileH = height;
|
|
1548
|
+
} else {
|
|
1549
|
+
tileW = image.width;
|
|
1550
|
+
tileH = image.height;
|
|
1551
|
+
}
|
|
1552
|
+
for (let ty = y; ty < y + height; ty += tileH) {
|
|
1553
|
+
for (let tx = x; tx < x + width; tx += tileW) {
|
|
1554
|
+
ctx.drawImage(image, tx, ty, tileW, tileH);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (debug) {
|
|
1562
|
+
ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";
|
|
1563
|
+
ctx.lineWidth = 1;
|
|
1564
|
+
ctx.strokeRect(x, y, width, height);
|
|
1565
|
+
}
|
|
1566
|
+
if (node.textContent !== void 0 && node.textContent !== "") {
|
|
1567
|
+
const paddingTop = toNumber2(style.paddingTop);
|
|
1568
|
+
const paddingLeft = toNumber2(style.paddingLeft);
|
|
1569
|
+
const paddingRight = toNumber2(style.paddingRight);
|
|
1570
|
+
const borderTopW = toNumber2(style.borderTopWidth);
|
|
1571
|
+
const borderLeftW = toNumber2(style.borderLeftWidth);
|
|
1572
|
+
const borderRightW = toNumber2(style.borderRightWidth);
|
|
1573
|
+
const contentX = x + paddingLeft + borderLeftW;
|
|
1574
|
+
const contentY = y + paddingTop + borderTopW;
|
|
1575
|
+
const contentWidth = width - paddingLeft - paddingRight - borderLeftW - borderRightW;
|
|
1576
|
+
const textLayout = layoutText(
|
|
1577
|
+
node.textContent,
|
|
1578
|
+
style,
|
|
1579
|
+
contentWidth,
|
|
1580
|
+
ctx,
|
|
1581
|
+
!!emojiStyle
|
|
1582
|
+
);
|
|
1583
|
+
await drawText(
|
|
1584
|
+
ctx,
|
|
1585
|
+
textLayout.segments,
|
|
1586
|
+
contentX,
|
|
1587
|
+
contentY,
|
|
1588
|
+
style.textShadow,
|
|
1589
|
+
emojiStyle
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
if (node.type === "img" && node.props.src) {
|
|
1593
|
+
const paddingTop = toNumber2(style.paddingTop);
|
|
1594
|
+
const paddingLeft = toNumber2(style.paddingLeft);
|
|
1595
|
+
const paddingRight = toNumber2(style.paddingRight);
|
|
1596
|
+
const paddingBottom = toNumber2(style.paddingBottom);
|
|
1597
|
+
const imgX = x + paddingLeft;
|
|
1598
|
+
const imgY = y + paddingTop;
|
|
1599
|
+
const imgW = width - paddingLeft - paddingRight;
|
|
1600
|
+
const imgH = height - paddingTop - paddingBottom;
|
|
1601
|
+
if (!isClipped) {
|
|
1602
|
+
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1603
|
+
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1604
|
+
applyClip(ctx, imgX, imgY, imgW, imgH, borderRadius);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
await drawImage(
|
|
1608
|
+
ctx,
|
|
1609
|
+
node.props.src,
|
|
1610
|
+
imgX,
|
|
1611
|
+
imgY,
|
|
1612
|
+
imgW,
|
|
1613
|
+
imgH,
|
|
1614
|
+
style,
|
|
1615
|
+
node.props.__loadedImage
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
if (node.type === "svg") {
|
|
1619
|
+
drawSvgContainer(ctx, node, x, y, width, height);
|
|
1620
|
+
} else {
|
|
1621
|
+
for (const child of node.children) {
|
|
1622
|
+
await drawNodeInner(ctx, child, x, y, 0, 0, debug, emojiStyle, void 0);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
ctx.restore();
|
|
1626
|
+
}
|
|
1347
1627
|
function applyTransform(ctx, transform, x, y, width, height, transformOrigin) {
|
|
1348
1628
|
let ox = x + width / 2;
|
|
1349
1629
|
let oy = y + height / 2;
|
|
@@ -1435,6 +1715,9 @@ function ensureFontsRegistered(fonts) {
|
|
|
1435
1715
|
}
|
|
1436
1716
|
}
|
|
1437
1717
|
|
|
1718
|
+
// src/jsx/layout.ts
|
|
1719
|
+
import { loadImage as loadImage4 } from "@napi-rs/canvas";
|
|
1720
|
+
|
|
1438
1721
|
// src/jsx/style/expand.ts
|
|
1439
1722
|
var SIDES = ["Top", "Right", "Bottom", "Left"];
|
|
1440
1723
|
function parseSides(value) {
|
|
@@ -1643,6 +1926,99 @@ function resolveStyle(rawStyle, parentStyle) {
|
|
|
1643
1926
|
}
|
|
1644
1927
|
return style;
|
|
1645
1928
|
}
|
|
1929
|
+
var DIMENSION_PROPS = [
|
|
1930
|
+
"width",
|
|
1931
|
+
"height",
|
|
1932
|
+
"minWidth",
|
|
1933
|
+
"minHeight",
|
|
1934
|
+
"maxWidth",
|
|
1935
|
+
"maxHeight",
|
|
1936
|
+
"top",
|
|
1937
|
+
"right",
|
|
1938
|
+
"bottom",
|
|
1939
|
+
"left",
|
|
1940
|
+
"marginTop",
|
|
1941
|
+
"marginRight",
|
|
1942
|
+
"marginBottom",
|
|
1943
|
+
"marginLeft",
|
|
1944
|
+
"paddingTop",
|
|
1945
|
+
"paddingRight",
|
|
1946
|
+
"paddingBottom",
|
|
1947
|
+
"paddingLeft",
|
|
1948
|
+
"rowGap",
|
|
1949
|
+
"columnGap",
|
|
1950
|
+
"flexBasis"
|
|
1951
|
+
];
|
|
1952
|
+
function resolveUnit(value, viewportWidth, viewportHeight, fontSize, rootFontSize) {
|
|
1953
|
+
if (value.endsWith("%") || value === "auto") return value;
|
|
1954
|
+
if (value.endsWith("vmin")) {
|
|
1955
|
+
const n = parseFloat(value);
|
|
1956
|
+
return isNaN(n) ? value : n / 100 * Math.min(viewportWidth, viewportHeight);
|
|
1957
|
+
}
|
|
1958
|
+
if (value.endsWith("vmax")) {
|
|
1959
|
+
const n = parseFloat(value);
|
|
1960
|
+
return isNaN(n) ? value : n / 100 * Math.max(viewportWidth, viewportHeight);
|
|
1961
|
+
}
|
|
1962
|
+
if (value.endsWith("vw")) {
|
|
1963
|
+
const n = parseFloat(value);
|
|
1964
|
+
return isNaN(n) ? value : n / 100 * viewportWidth;
|
|
1965
|
+
}
|
|
1966
|
+
if (value.endsWith("vh")) {
|
|
1967
|
+
const n = parseFloat(value);
|
|
1968
|
+
return isNaN(n) ? value : n / 100 * viewportHeight;
|
|
1969
|
+
}
|
|
1970
|
+
if (value.endsWith("rem")) {
|
|
1971
|
+
const n = parseFloat(value);
|
|
1972
|
+
return isNaN(n) ? value : n * rootFontSize;
|
|
1973
|
+
}
|
|
1974
|
+
if (value.endsWith("em")) {
|
|
1975
|
+
const n = parseFloat(value);
|
|
1976
|
+
return isNaN(n) ? value : n * fontSize;
|
|
1977
|
+
}
|
|
1978
|
+
if (value.endsWith("px")) {
|
|
1979
|
+
const n = parseFloat(value);
|
|
1980
|
+
return isNaN(n) ? value : n;
|
|
1981
|
+
}
|
|
1982
|
+
if (value.endsWith("pt")) {
|
|
1983
|
+
const n = parseFloat(value);
|
|
1984
|
+
return isNaN(n) ? value : n * (96 / 72);
|
|
1985
|
+
}
|
|
1986
|
+
if (value.endsWith("pc")) {
|
|
1987
|
+
const n = parseFloat(value);
|
|
1988
|
+
return isNaN(n) ? value : n * 16;
|
|
1989
|
+
}
|
|
1990
|
+
if (value.endsWith("in")) {
|
|
1991
|
+
const n = parseFloat(value);
|
|
1992
|
+
return isNaN(n) ? value : n * 96;
|
|
1993
|
+
}
|
|
1994
|
+
if (value.endsWith("cm")) {
|
|
1995
|
+
const n = parseFloat(value);
|
|
1996
|
+
return isNaN(n) ? value : n * (96 / 2.54);
|
|
1997
|
+
}
|
|
1998
|
+
if (value.endsWith("mm")) {
|
|
1999
|
+
const n = parseFloat(value);
|
|
2000
|
+
return isNaN(n) ? value : n * (96 / 25.4);
|
|
2001
|
+
}
|
|
2002
|
+
return value;
|
|
2003
|
+
}
|
|
2004
|
+
function resolveUnits(style, viewportWidth, viewportHeight, rootFontSize = DEFAULT_STYLE.fontSize) {
|
|
2005
|
+
const fontSize = typeof style.fontSize === "number" ? style.fontSize : rootFontSize;
|
|
2006
|
+
for (const prop of DIMENSION_PROPS) {
|
|
2007
|
+
const value = style[prop];
|
|
2008
|
+
if (typeof value !== "string") continue;
|
|
2009
|
+
const resolved = resolveUnit(
|
|
2010
|
+
value,
|
|
2011
|
+
viewportWidth,
|
|
2012
|
+
viewportHeight,
|
|
2013
|
+
fontSize,
|
|
2014
|
+
rootFontSize
|
|
2015
|
+
);
|
|
2016
|
+
if (resolved !== value) {
|
|
2017
|
+
style[prop] = resolved;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
return style;
|
|
2021
|
+
}
|
|
1646
2022
|
|
|
1647
2023
|
// src/jsx/yoga.ts
|
|
1648
2024
|
import Yoga, {
|
|
@@ -1831,12 +2207,14 @@ function applyEdgeValue(node, setter, edge, value) {
|
|
|
1831
2207
|
}
|
|
1832
2208
|
|
|
1833
2209
|
// src/jsx/layout.ts
|
|
1834
|
-
function buildLayoutTree(element, containerWidth, containerHeight, ctx, emojiEnabled) {
|
|
2210
|
+
async function buildLayoutTree(element, containerWidth, containerHeight, ctx, emojiEnabled) {
|
|
1835
2211
|
const rootYogaNode = createYogaNode();
|
|
1836
|
-
const rootNode = buildNode(
|
|
2212
|
+
const rootNode = await buildNode(
|
|
1837
2213
|
element,
|
|
1838
2214
|
DEFAULT_STYLE,
|
|
1839
2215
|
rootYogaNode,
|
|
2216
|
+
containerWidth,
|
|
2217
|
+
containerHeight,
|
|
1840
2218
|
ctx,
|
|
1841
2219
|
emojiEnabled
|
|
1842
2220
|
);
|
|
@@ -1847,7 +2225,7 @@ function buildLayoutTree(element, containerWidth, containerHeight, ctx, emojiEna
|
|
|
1847
2225
|
freeYogaNode(rootYogaNode);
|
|
1848
2226
|
return layoutTree;
|
|
1849
2227
|
}
|
|
1850
|
-
function buildNode(element, parentStyle, yogaNode, ctx, emojiEnabled) {
|
|
2228
|
+
async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewportHeight, ctx, emojiEnabled) {
|
|
1851
2229
|
if (element === null || element === void 0 || typeof element === "boolean") {
|
|
1852
2230
|
return {
|
|
1853
2231
|
type: "empty",
|
|
@@ -1880,7 +2258,17 @@ function buildNode(element, parentStyle, yogaNode, ctx, emojiEnabled) {
|
|
|
1880
2258
|
continue;
|
|
1881
2259
|
const childYogaNode = createYogaNode();
|
|
1882
2260
|
yogaNode.insertChild(childYogaNode, children2.length);
|
|
1883
|
-
children2.push(
|
|
2261
|
+
children2.push(
|
|
2262
|
+
await buildNode(
|
|
2263
|
+
child,
|
|
2264
|
+
style2,
|
|
2265
|
+
childYogaNode,
|
|
2266
|
+
viewportWidth,
|
|
2267
|
+
viewportHeight,
|
|
2268
|
+
ctx,
|
|
2269
|
+
emojiEnabled
|
|
2270
|
+
)
|
|
2271
|
+
);
|
|
1884
2272
|
}
|
|
1885
2273
|
return {
|
|
1886
2274
|
type: "div",
|
|
@@ -1896,12 +2284,21 @@ function buildNode(element, parentStyle, yogaNode, ctx, emojiEnabled) {
|
|
|
1896
2284
|
const rendered = type(
|
|
1897
2285
|
el.props ?? {}
|
|
1898
2286
|
);
|
|
1899
|
-
return buildNode(
|
|
2287
|
+
return await buildNode(
|
|
2288
|
+
rendered,
|
|
2289
|
+
parentStyle,
|
|
2290
|
+
yogaNode,
|
|
2291
|
+
viewportWidth,
|
|
2292
|
+
viewportHeight,
|
|
2293
|
+
ctx,
|
|
2294
|
+
emojiEnabled
|
|
2295
|
+
);
|
|
1900
2296
|
}
|
|
1901
2297
|
const props = el.props ?? {};
|
|
1902
2298
|
const rawStyle = props.style ?? {};
|
|
1903
2299
|
const expanded = expandStyle(rawStyle);
|
|
1904
2300
|
const style = resolveStyle(expanded, parentStyle);
|
|
2301
|
+
resolveUnits(style, viewportWidth, viewportHeight);
|
|
1905
2302
|
const tagName = String(type);
|
|
1906
2303
|
if (tagName === "svg") {
|
|
1907
2304
|
if (props.width != null && style.width === void 0)
|
|
@@ -1928,6 +2325,32 @@ function buildNode(element, parentStyle, yogaNode, ctx, emojiEnabled) {
|
|
|
1928
2325
|
}
|
|
1929
2326
|
}
|
|
1930
2327
|
}
|
|
2328
|
+
if (tagName === "img") {
|
|
2329
|
+
if (props.width != null && style.width === void 0)
|
|
2330
|
+
style.width = Number(props.width);
|
|
2331
|
+
if (props.height != null && style.height === void 0)
|
|
2332
|
+
style.height = Number(props.height);
|
|
2333
|
+
const src = props.src;
|
|
2334
|
+
if (src) {
|
|
2335
|
+
try {
|
|
2336
|
+
const image = await loadImage4(src);
|
|
2337
|
+
const naturalW = image.width;
|
|
2338
|
+
const naturalH = image.height;
|
|
2339
|
+
const w = typeof style.width === "number" ? style.width : void 0;
|
|
2340
|
+
const h = typeof style.height === "number" ? style.height : void 0;
|
|
2341
|
+
if (w !== void 0 && h === void 0 && naturalW > 0) {
|
|
2342
|
+
style.height = w * (naturalH / naturalW);
|
|
2343
|
+
} else if (h !== void 0 && w === void 0 && naturalH > 0) {
|
|
2344
|
+
style.width = h * (naturalW / naturalH);
|
|
2345
|
+
} else if (w === void 0 && h === void 0) {
|
|
2346
|
+
style.width = naturalW;
|
|
2347
|
+
style.height = naturalH;
|
|
2348
|
+
}
|
|
2349
|
+
props.__loadedImage = image;
|
|
2350
|
+
} catch {
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
1931
2354
|
applyStylesToYoga(yogaNode, style);
|
|
1932
2355
|
if (tagName === "svg") {
|
|
1933
2356
|
return {
|
|
@@ -1976,7 +2399,17 @@ function buildNode(element, parentStyle, yogaNode, ctx, emojiEnabled) {
|
|
|
1976
2399
|
continue;
|
|
1977
2400
|
const childYogaNode = createYogaNode();
|
|
1978
2401
|
yogaNode.insertChild(childYogaNode, children.length);
|
|
1979
|
-
children.push(
|
|
2402
|
+
children.push(
|
|
2403
|
+
await buildNode(
|
|
2404
|
+
child,
|
|
2405
|
+
style,
|
|
2406
|
+
childYogaNode,
|
|
2407
|
+
viewportWidth,
|
|
2408
|
+
viewportHeight,
|
|
2409
|
+
ctx,
|
|
2410
|
+
emojiEnabled
|
|
2411
|
+
)
|
|
2412
|
+
);
|
|
1980
2413
|
}
|
|
1981
2414
|
}
|
|
1982
2415
|
return {
|
|
@@ -2042,12 +2475,18 @@ async function renderReactElement(ctx, element, options) {
|
|
|
2042
2475
|
const width = ctx.canvas.width;
|
|
2043
2476
|
const height = ctx.canvas.height;
|
|
2044
2477
|
const emojiStyle = options.emoji === "none" ? void 0 : options.emoji ?? "twemoji";
|
|
2045
|
-
const layoutTree = buildLayoutTree(
|
|
2478
|
+
const layoutTree = await buildLayoutTree(
|
|
2479
|
+
element,
|
|
2480
|
+
width,
|
|
2481
|
+
height,
|
|
2482
|
+
ctx,
|
|
2483
|
+
!!emojiStyle
|
|
2484
|
+
);
|
|
2046
2485
|
await drawNode(ctx, layoutTree, 0, 0, options.debug ?? false, emojiStyle);
|
|
2047
2486
|
}
|
|
2048
2487
|
|
|
2049
2488
|
// src/index.ts
|
|
2050
|
-
function
|
|
2489
|
+
function createCanvas3(width, height) {
|
|
2051
2490
|
const canvas = _createCanvas(width, height);
|
|
2052
2491
|
const origEncode = canvas.encode.bind(canvas);
|
|
2053
2492
|
canvas.encode = (async (...args) => Buffer.from(await origEncode(...args)));
|
|
@@ -2058,8 +2497,8 @@ export {
|
|
|
2058
2497
|
GlobalFonts2 as GlobalFonts,
|
|
2059
2498
|
Image,
|
|
2060
2499
|
LottieAnimation,
|
|
2061
|
-
|
|
2062
|
-
|
|
2500
|
+
createCanvas3 as createCanvas,
|
|
2501
|
+
loadImage5 as loadImage,
|
|
2063
2502
|
loadLottie,
|
|
2064
2503
|
registerFont,
|
|
2065
2504
|
registerFontFromPath,
|