@effing/canvas 0.24.5 → 0.24.6
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 +72 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -267,22 +267,21 @@ function parseFontMetrics(data) {
|
|
|
267
267
|
const numTables = view.getUint16(4);
|
|
268
268
|
if (buf.byteLength < 12 + numTables * 16) return null;
|
|
269
269
|
let headOffset = -1;
|
|
270
|
-
let
|
|
270
|
+
let hheaOffset = -1;
|
|
271
271
|
for (let i = 0; i < numTables; i++) {
|
|
272
272
|
const recordOffset = 12 + i * 16;
|
|
273
273
|
const tag = String.fromCharCode(view.getUint8(recordOffset)) + String.fromCharCode(view.getUint8(recordOffset + 1)) + String.fromCharCode(view.getUint8(recordOffset + 2)) + String.fromCharCode(view.getUint8(recordOffset + 3));
|
|
274
274
|
const offset = view.getUint32(recordOffset + 8);
|
|
275
275
|
if (tag === "head") headOffset = offset;
|
|
276
|
-
else if (tag === "
|
|
276
|
+
else if (tag === "hhea") hheaOffset = offset;
|
|
277
277
|
}
|
|
278
|
-
if (headOffset < 0 ||
|
|
278
|
+
if (headOffset < 0 || hheaOffset < 0) return null;
|
|
279
279
|
if (buf.byteLength < headOffset + 20) return null;
|
|
280
280
|
const unitsPerEm = view.getUint16(headOffset + 18);
|
|
281
|
-
if (buf.byteLength <
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
return { unitsPerEm, sTypoAscender, sTypoDescender, sTypoLineGap };
|
|
281
|
+
if (buf.byteLength < hheaOffset + 8) return null;
|
|
282
|
+
const ascender = view.getInt16(hheaOffset + 4);
|
|
283
|
+
const descender = view.getInt16(hheaOffset + 6);
|
|
284
|
+
return { unitsPerEm, ascender, descender };
|
|
286
285
|
}
|
|
287
286
|
|
|
288
287
|
// src/jsx/font.ts
|
|
@@ -413,8 +412,8 @@ function measureTrimMetrics(fontSize, fontFamily, fontWeight, fontStyle, lineHei
|
|
|
413
412
|
let fontAscent;
|
|
414
413
|
let fontDescent;
|
|
415
414
|
if (fontMetrics) {
|
|
416
|
-
fontAscent = fontMetrics.
|
|
417
|
-
fontDescent = -fontMetrics.
|
|
415
|
+
fontAscent = fontMetrics.ascender / fontMetrics.unitsPerEm * fontSize;
|
|
416
|
+
fontDescent = -fontMetrics.descender / fontMetrics.unitsPerEm * fontSize;
|
|
418
417
|
} else {
|
|
419
418
|
fontAscent = refMetrics.fontBoundingBoxAscent ?? refMetrics.actualBoundingBoxAscent ?? fontSize * 0.8;
|
|
420
419
|
fontDescent = refMetrics.fontBoundingBoxDescent ?? refMetrics.actualBoundingBoxDescent ?? fontSize * 0.2;
|
|
@@ -727,8 +726,8 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
|
|
|
727
726
|
function resolveLineHeight(lineHeight, fontSize, canvasMetrics, fontMetrics) {
|
|
728
727
|
if (lineHeight === void 0 || lineHeight === "normal") {
|
|
729
728
|
if (fontMetrics) {
|
|
730
|
-
const {
|
|
731
|
-
return (
|
|
729
|
+
const { ascender, descender, unitsPerEm } = fontMetrics;
|
|
730
|
+
return (ascender - descender) / unitsPerEm * fontSize;
|
|
732
731
|
}
|
|
733
732
|
return canvasMetrics ? canvasMetrics.ascent + canvasMetrics.descent : fontSize * 1.2;
|
|
734
733
|
}
|
|
@@ -2325,6 +2324,7 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
2325
2324
|
|
|
2326
2325
|
// src/jsx/draw/text.ts
|
|
2327
2326
|
import { loadImage as loadImage2 } from "@napi-rs/canvas";
|
|
2327
|
+
import parseCssColor2 from "parse-css-color";
|
|
2328
2328
|
|
|
2329
2329
|
// src/jsx/emoji.ts
|
|
2330
2330
|
var emojiApis = {
|
|
@@ -2440,12 +2440,14 @@ function loadEmojiImage(style, char) {
|
|
|
2440
2440
|
return cached;
|
|
2441
2441
|
}
|
|
2442
2442
|
async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle) {
|
|
2443
|
+
const shadow = textShadow ? parseShadow(textShadow) : null;
|
|
2443
2444
|
for (const seg of segments) {
|
|
2444
2445
|
if (!seg.text) continue;
|
|
2445
2446
|
setFont(ctx, seg.fontSize, seg.fontFamily, seg.fontWeight, seg.fontStyle);
|
|
2446
2447
|
ctx.fillStyle = seg.color;
|
|
2447
2448
|
const x = offsetX + seg.x;
|
|
2448
2449
|
const y = offsetY + seg.y;
|
|
2450
|
+
const textAlpha = shadow ? getColorAlpha(seg.color) : 1;
|
|
2449
2451
|
const hasStroke = seg.textStrokeWidth !== void 0 && seg.textStrokeWidth > 0;
|
|
2450
2452
|
if (emojiStyle) {
|
|
2451
2453
|
await drawSegmentWithEmoji(
|
|
@@ -2458,6 +2460,14 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
2458
2460
|
hasStroke
|
|
2459
2461
|
);
|
|
2460
2462
|
} else if (seg.letterSpacing && seg.letterSpacing !== 0) {
|
|
2463
|
+
if (shadow) {
|
|
2464
|
+
drawShadowPass(
|
|
2465
|
+
ctx,
|
|
2466
|
+
shadow,
|
|
2467
|
+
textAlpha,
|
|
2468
|
+
() => drawTextWithLetterSpacing(ctx, seg.text, x, y, seg.letterSpacing)
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2461
2471
|
if (hasStroke) {
|
|
2462
2472
|
drawStrokeWithLetterSpacing(
|
|
2463
2473
|
ctx,
|
|
@@ -2471,8 +2481,13 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
2471
2481
|
}
|
|
2472
2482
|
drawTextWithLetterSpacing(ctx, seg.text, x, y, seg.letterSpacing);
|
|
2473
2483
|
} else {
|
|
2474
|
-
if (
|
|
2475
|
-
|
|
2484
|
+
if (shadow) {
|
|
2485
|
+
drawShadowPass(
|
|
2486
|
+
ctx,
|
|
2487
|
+
shadow,
|
|
2488
|
+
textAlpha,
|
|
2489
|
+
() => ctx.fillText(seg.text, x, y)
|
|
2490
|
+
);
|
|
2476
2491
|
}
|
|
2477
2492
|
if (hasStroke) {
|
|
2478
2493
|
ctx.save();
|
|
@@ -2500,10 +2515,32 @@ async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle, hasS
|
|
|
2500
2515
|
seg.fontSize,
|
|
2501
2516
|
letterSpacing
|
|
2502
2517
|
);
|
|
2518
|
+
const shadow = textShadow ? parseShadow(textShadow) : null;
|
|
2519
|
+
const textAlpha = shadow ? getColorAlpha(seg.color) : 1;
|
|
2503
2520
|
for (const run of runs) {
|
|
2504
2521
|
if (run.kind === "text") {
|
|
2505
|
-
if (
|
|
2506
|
-
|
|
2522
|
+
if (shadow) {
|
|
2523
|
+
if (letterSpacing !== 0) {
|
|
2524
|
+
drawShadowPass(
|
|
2525
|
+
ctx,
|
|
2526
|
+
shadow,
|
|
2527
|
+
textAlpha,
|
|
2528
|
+
() => drawTextWithLetterSpacing(
|
|
2529
|
+
ctx,
|
|
2530
|
+
run.text,
|
|
2531
|
+
x + run.x,
|
|
2532
|
+
y,
|
|
2533
|
+
letterSpacing
|
|
2534
|
+
)
|
|
2535
|
+
);
|
|
2536
|
+
} else {
|
|
2537
|
+
drawShadowPass(
|
|
2538
|
+
ctx,
|
|
2539
|
+
shadow,
|
|
2540
|
+
textAlpha,
|
|
2541
|
+
() => ctx.fillText(run.text, x + run.x, y)
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2507
2544
|
}
|
|
2508
2545
|
if (hasStroke) {
|
|
2509
2546
|
if (letterSpacing !== 0) {
|
|
@@ -2563,17 +2600,29 @@ function drawStrokeWithLetterSpacing(ctx, text, x, y, letterSpacing, strokeWidth
|
|
|
2563
2600
|
}
|
|
2564
2601
|
ctx.restore();
|
|
2565
2602
|
}
|
|
2566
|
-
function
|
|
2603
|
+
function parseShadow(shadow) {
|
|
2567
2604
|
const parts = shadow.match(
|
|
2568
2605
|
/(-?\d+(?:\.\d+)?)\s*(?:px)?\s+(-?\d+(?:\.\d+)?)\s*(?:px)?\s+(-?\d+(?:\.\d+)?)\s*(?:px)?\s+(.*)/
|
|
2569
2606
|
);
|
|
2570
|
-
if (!parts) return;
|
|
2607
|
+
if (!parts) return null;
|
|
2608
|
+
return {
|
|
2609
|
+
offsetX: parseFloat(parts[1]),
|
|
2610
|
+
offsetY: parseFloat(parts[2]),
|
|
2611
|
+
blur: parseFloat(parts[3]),
|
|
2612
|
+
color: parts[4].trim()
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
function getColorAlpha(color) {
|
|
2616
|
+
const parsed = parseCssColor2(color);
|
|
2617
|
+
return parsed ? parsed.alpha : 1;
|
|
2618
|
+
}
|
|
2619
|
+
function drawShadowPass(ctx, shadow, textAlpha, drawFn) {
|
|
2571
2620
|
ctx.save();
|
|
2572
|
-
ctx.
|
|
2573
|
-
ctx.
|
|
2574
|
-
ctx.
|
|
2575
|
-
ctx.
|
|
2576
|
-
|
|
2621
|
+
ctx.fillStyle = shadow.color;
|
|
2622
|
+
if (textAlpha < 1) ctx.globalAlpha *= textAlpha;
|
|
2623
|
+
if (shadow.blur > 0) ctx.filter = `blur(${shadow.blur / 2}px)`;
|
|
2624
|
+
ctx.translate(shadow.offsetX, shadow.offsetY);
|
|
2625
|
+
drawFn();
|
|
2577
2626
|
ctx.restore();
|
|
2578
2627
|
}
|
|
2579
2628
|
function drawTextDecoration(ctx, seg, offsetX, offsetY) {
|