@effing/canvas 0.24.6 → 0.24.8

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 CHANGED
@@ -260,12 +260,67 @@ function resolveTransformUnits(transform, viewportWidth, viewportHeight, fontSiz
260
260
  import { GlobalFonts } from "@napi-rs/canvas";
261
261
 
262
262
  // src/jsx/font-metrics.ts
263
- function parseFontMetrics(data) {
264
- const buf = data instanceof ArrayBuffer ? data : data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
265
- const view = new DataView(buf);
266
- if (buf.byteLength < 12) return null;
263
+ import { inflateSync } from "zlib";
264
+ function fontMetricsToPx(metrics, fontSize) {
265
+ return {
266
+ ascent: metrics.ascender / metrics.unitsPerEm * fontSize,
267
+ descent: -metrics.descender / metrics.unitsPerEm * fontSize
268
+ };
269
+ }
270
+ var WOFF_SIGNATURE = 2001684038;
271
+ function readWoffTable(view, entry) {
272
+ const compressed = entry.compLength < entry.origLength;
273
+ if (compressed) {
274
+ try {
275
+ const compData = new Uint8Array(
276
+ view.buffer,
277
+ view.byteOffset + entry.offset,
278
+ entry.compLength
279
+ );
280
+ const decompressed = inflateSync(compData);
281
+ return new DataView(
282
+ decompressed.buffer,
283
+ decompressed.byteOffset,
284
+ decompressed.byteLength
285
+ );
286
+ } catch {
287
+ return null;
288
+ }
289
+ }
290
+ return new DataView(
291
+ view.buffer,
292
+ view.byteOffset + entry.offset,
293
+ entry.origLength
294
+ );
295
+ }
296
+ function parseWoff(view) {
297
+ if (view.byteLength < 44) return null;
298
+ const numTables = view.getUint16(12);
299
+ if (view.byteLength < 44 + numTables * 20) return null;
300
+ let headEntry = null;
301
+ let hheaEntry = null;
302
+ for (let i = 0; i < numTables; i++) {
303
+ const r = 44 + i * 20;
304
+ const tag = String.fromCharCode(view.getUint8(r)) + String.fromCharCode(view.getUint8(r + 1)) + String.fromCharCode(view.getUint8(r + 2)) + String.fromCharCode(view.getUint8(r + 3));
305
+ const offset = view.getUint32(r + 4);
306
+ const compLength = view.getUint32(r + 8);
307
+ const origLength = view.getUint32(r + 12);
308
+ if (tag === "head") headEntry = { offset, compLength, origLength };
309
+ else if (tag === "hhea") hheaEntry = { offset, compLength, origLength };
310
+ }
311
+ if (!headEntry || !hheaEntry) return null;
312
+ const headView = readWoffTable(view, headEntry);
313
+ const hheaView = readWoffTable(view, hheaEntry);
314
+ if (!headView || !hheaView) return null;
315
+ if (headView.byteLength < 20 || hheaView.byteLength < 8) return null;
316
+ const unitsPerEm = headView.getUint16(18);
317
+ const ascender = hheaView.getInt16(4);
318
+ const descender = hheaView.getInt16(6);
319
+ return { unitsPerEm, ascender, descender };
320
+ }
321
+ function parseSfnt(view) {
267
322
  const numTables = view.getUint16(4);
268
- if (buf.byteLength < 12 + numTables * 16) return null;
323
+ if (view.byteLength < 12 + numTables * 16) return null;
269
324
  let headOffset = -1;
270
325
  let hheaOffset = -1;
271
326
  for (let i = 0; i < numTables; i++) {
@@ -276,13 +331,23 @@ function parseFontMetrics(data) {
276
331
  else if (tag === "hhea") hheaOffset = offset;
277
332
  }
278
333
  if (headOffset < 0 || hheaOffset < 0) return null;
279
- if (buf.byteLength < headOffset + 20) return null;
334
+ if (view.byteLength < headOffset + 20) return null;
280
335
  const unitsPerEm = view.getUint16(headOffset + 18);
281
- if (buf.byteLength < hheaOffset + 8) return null;
336
+ if (view.byteLength < hheaOffset + 8) return null;
282
337
  const ascender = view.getInt16(hheaOffset + 4);
283
338
  const descender = view.getInt16(hheaOffset + 6);
284
339
  return { unitsPerEm, ascender, descender };
285
340
  }
341
+ function parseFontMetrics(data) {
342
+ const buf = data instanceof ArrayBuffer ? data : data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
343
+ const view = new DataView(buf);
344
+ if (buf.byteLength < 12) return null;
345
+ const signature = view.getUint32(0);
346
+ if (signature === WOFF_SIGNATURE) {
347
+ return parseWoff(view);
348
+ }
349
+ return parseSfnt(view);
350
+ }
286
351
 
287
352
  // src/jsx/font.ts
288
353
  var registeredFonts = /* @__PURE__ */ new Set();
@@ -299,10 +364,13 @@ function registerFont(font) {
299
364
  registeredFonts.add(key);
300
365
  }
301
366
  function getFontMetrics(family, weight, style) {
302
- const exact = metricsCache.get(`${family}:${weight}:${style}`);
303
- if (exact) return exact;
304
- for (const [key, metrics] of metricsCache) {
305
- if (key.startsWith(`${family}:`)) return metrics;
367
+ const families = family.split(",").map((f) => f.trim().replace(/^['"]|['"]$/g, ""));
368
+ for (const name of families) {
369
+ const exact = metricsCache.get(`${name}:${weight}:${style}`);
370
+ if (exact) return exact;
371
+ for (const [key, metrics] of metricsCache) {
372
+ if (key.startsWith(`${name}:`)) return metrics;
373
+ }
306
374
  }
307
375
  return null;
308
376
  }
@@ -412,8 +480,9 @@ function measureTrimMetrics(fontSize, fontFamily, fontWeight, fontStyle, lineHei
412
480
  let fontAscent;
413
481
  let fontDescent;
414
482
  if (fontMetrics) {
415
- fontAscent = fontMetrics.ascender / fontMetrics.unitsPerEm * fontSize;
416
- fontDescent = -fontMetrics.descender / fontMetrics.unitsPerEm * fontSize;
483
+ const px = fontMetricsToPx(fontMetrics, fontSize);
484
+ fontAscent = px.ascent;
485
+ fontDescent = px.descent;
417
486
  } else {
418
487
  fontAscent = refMetrics.fontBoundingBoxAscent ?? refMetrics.actualBoundingBoxAscent ?? fontSize * 0.8;
419
488
  fontDescent = refMetrics.fontBoundingBoxDescent ?? refMetrics.actualBoundingBoxDescent ?? fontSize * 0.2;
@@ -637,7 +706,8 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
637
706
  let totalHeight = 0;
638
707
  let maxLineWidth = 0;
639
708
  const isAutoLineHeight = style.lineHeight === void 0 || style.lineHeight === "normal";
640
- let lastMetrics;
709
+ const fontPx = fontMetrics ? fontMetricsToPx(fontMetrics, fontSize) : null;
710
+ let lastPositioningDescent;
641
711
  for (let i = 0; i < lines.length; i++) {
642
712
  const line = lines[i];
643
713
  const lineWidth = measure(line);
@@ -655,14 +725,16 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
655
725
  fontStyle,
656
726
  ctx
657
727
  );
658
- lastMetrics = metrics;
659
- const contentHeight = metrics.ascent + metrics.descent;
660
- let effectiveAscent = metrics.ascent;
661
- let effectiveDescent = metrics.descent;
728
+ const positioningAscent = fontPx?.ascent ?? metrics.ascent;
729
+ const positioningDescent = fontPx?.descent ?? metrics.descent;
730
+ lastPositioningDescent = positioningDescent;
731
+ const contentHeight = positioningAscent + positioningDescent;
732
+ let effectiveAscent = positioningAscent;
733
+ let effectiveDescent = positioningDescent;
662
734
  if (isAutoLineHeight && contentHeight > lineHeightPx) {
663
735
  const scale = lineHeightPx / contentHeight;
664
- effectiveAscent = metrics.ascent * scale;
665
- effectiveDescent = metrics.descent * scale;
736
+ effectiveAscent = positioningAscent * scale;
737
+ effectiveDescent = positioningDescent * scale;
666
738
  }
667
739
  const baselineY = totalHeight + (lineHeightPx + effectiveAscent - effectiveDescent) / 2;
668
740
  segments.push({
@@ -686,9 +758,9 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
686
758
  totalHeight += lineHeightPx;
687
759
  maxLineWidth = Math.max(maxLineWidth, lineWidth);
688
760
  }
689
- if (isAutoLineHeight && lastMetrics && segments.length > 0) {
761
+ if (isAutoLineHeight && lastPositioningDescent !== void 0 && segments.length > 0) {
690
762
  const lastSeg = segments[segments.length - 1];
691
- const descentOverflow = lastSeg.y + lastMetrics.descent - totalHeight;
763
+ const descentOverflow = lastSeg.y + lastPositioningDescent - totalHeight;
692
764
  if (descentOverflow > 0) {
693
765
  totalHeight += descentOverflow;
694
766
  }
@@ -726,8 +798,8 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
726
798
  function resolveLineHeight(lineHeight, fontSize, canvasMetrics, fontMetrics) {
727
799
  if (lineHeight === void 0 || lineHeight === "normal") {
728
800
  if (fontMetrics) {
729
- const { ascender, descender, unitsPerEm } = fontMetrics;
730
- return (ascender - descender) / unitsPerEm * fontSize;
801
+ const px = fontMetricsToPx(fontMetrics, fontSize);
802
+ return px.ascent + px.descent;
731
803
  }
732
804
  return canvasMetrics ? canvasMetrics.ascent + canvasMetrics.descent : fontSize * 1.2;
733
805
  }