@pixldocs/canvas-renderer 0.5.110 → 0.5.112

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.cjs CHANGED
@@ -15071,6 +15071,7 @@ function resolveFontWeight(weight) {
15071
15071
  }
15072
15072
  const FONT_FALLBACK_SYMBOLS = "Noto Sans";
15073
15073
  const FONT_FALLBACK_DEVANAGARI = "Hind";
15074
+ const FONT_FALLBACK_MATH = "Noto Sans Math";
15074
15075
  const FONT_FILES = {
15075
15076
  "Playfair Display": {
15076
15077
  regular: "PlayfairDisplay-Regular.ttf",
@@ -15542,6 +15543,9 @@ const FONT_FILES = {
15542
15543
  medium: "Hind-Medium.ttf",
15543
15544
  semibold: "Hind-SemiBold.ttf"
15544
15545
  },
15546
+ "Noto Sans Math": {
15547
+ regular: "NotoSansMath-Regular.ttf"
15548
+ },
15545
15549
  "Cinzel": { regular: "Cinzel-Regular.ttf", bold: "Cinzel-Bold.ttf" },
15546
15550
  "Cormorant Garamond": {
15547
15551
  regular: "CormorantGaramond-Regular.ttf",
@@ -15644,6 +15648,54 @@ function isJsPdfEmbeddableTrueType(bytes) {
15644
15648
  }
15645
15649
  return false;
15646
15650
  }
15651
+ function base64ToBytes(b64) {
15652
+ const binary = atob(b64);
15653
+ const bytes = new Uint8Array(binary.length);
15654
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
15655
+ return bytes;
15656
+ }
15657
+ function extractSupportedCodePointsFromTtf(bytes) {
15658
+ const out = /* @__PURE__ */ new Set();
15659
+ if (bytes.length < 12) return out;
15660
+ const u16 = (o) => bytes[o] << 8 | bytes[o + 1];
15661
+ const i16 = (o) => {
15662
+ const v = u16(o);
15663
+ return v & 32768 ? v - 65536 : v;
15664
+ };
15665
+ const u32 = (o) => (bytes[o] << 24 | bytes[o + 1] << 16 | bytes[o + 2] << 8 | bytes[o + 3]) >>> 0;
15666
+ let cmap = 0;
15667
+ for (let i = 0, n = u16(4); i < n; i++) {
15668
+ const off = 12 + i * 16;
15669
+ if (String.fromCharCode(bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]) === "cmap") {
15670
+ cmap = u32(off + 8);
15671
+ break;
15672
+ }
15673
+ }
15674
+ if (!cmap) return out;
15675
+ const candidates = [];
15676
+ for (let i = 0, n = u16(cmap + 2); i < n; i++) {
15677
+ const enc = cmap + 4 + i * 8;
15678
+ const platform = u16(enc), encoding = u16(enc + 2), off = cmap + u32(enc + 4), format = u16(off);
15679
+ const score = (format === 12 ? 40 : format === 4 ? 30 : 0) + (platform === 3 && encoding === 10 ? 2 : platform === 0 ? 1 : 0);
15680
+ if (score) candidates.push({ format, off, score });
15681
+ }
15682
+ candidates.sort((a, b) => b.score - a.score);
15683
+ const best = candidates[0];
15684
+ if (!best) return out;
15685
+ if (best.format === 12) {
15686
+ for (let i = 0, n = u32(best.off + 12); i < n; i++) {
15687
+ const off = best.off + 16 + i * 12, start = u32(off), end = u32(off + 4);
15688
+ for (let cp = start; cp <= end && cp <= 1114111; cp++) out.add(cp);
15689
+ }
15690
+ } else if (best.format === 4) {
15691
+ const segCount = u16(best.off + 6) / 2, endCodes = best.off + 14, startCodes = endCodes + segCount * 2 + 2, deltas = startCodes + segCount * 2, ranges = deltas + segCount * 2;
15692
+ for (let i = 0; i < segCount; i++) for (let cp = u16(startCodes + i * 2), end = u16(endCodes + i * 2); cp <= end && cp !== 65535; cp++) {
15693
+ const ro = u16(ranges + i * 2);
15694
+ if (ro === 0 ? (cp + i16(deltas + i * 2) & 65535) !== 0 : u16(ranges + i * 2 + ro + (cp - u16(startCodes + i * 2)) * 2) !== 0) out.add(cp);
15695
+ }
15696
+ }
15697
+ return out;
15698
+ }
15647
15699
  async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
15648
15700
  const fontFiles = FONT_FILES[fontName];
15649
15701
  if (!fontFiles) return false;
@@ -15660,6 +15712,7 @@ async function embedFont(pdf, fontName, weight, fontBaseUrl, isItalic = false) {
15660
15712
  try {
15661
15713
  const b64 = await fetchTTFAsBase64(url);
15662
15714
  if (!b64) return false;
15715
+ registeredVariantCoverage.set(variantKey(fontName, resolvedWeight, isItalic), extractSupportedCodePointsFromTtf(base64ToBytes(b64)));
15663
15716
  pdf.addFileToVFS(fileName, b64);
15664
15717
  pdf.addFont(fileName, jsPdfFontName, "normal");
15665
15718
  if (fontName !== jsPdfFontName) {
@@ -15680,6 +15733,7 @@ const googleFontNotFound = /* @__PURE__ */ new Set();
15680
15733
  const fontshareNotFound = /* @__PURE__ */ new Set();
15681
15734
  const remoteVariantKey = (family, weight, isItalic) => `${family}|${resolveFontWeight(weight)}|${isItalic ? "i" : "n"}`;
15682
15735
  const registeredVariants = /* @__PURE__ */ new Set();
15736
+ const registeredVariantCoverage = /* @__PURE__ */ new Map();
15683
15737
  const variantKey = (family, weight, italic) => `${family}|${resolveFontWeight(weight)}|${italic ? "i" : "n"}`;
15684
15738
  const resolveBestRegisteredVariant = (family, weight, italic) => {
15685
15739
  const want = resolveFontWeight(weight);
@@ -15690,6 +15744,12 @@ const resolveBestRegisteredVariant = (family, weight, italic) => {
15690
15744
  for (const w of nearest) if (registeredVariants.has(variantKey(family, w, !italic))) return { weight: w, italic: !italic };
15691
15745
  return null;
15692
15746
  };
15747
+ const doesVariantSupportChar = (family, weight, italic, char) => {
15748
+ var _a;
15749
+ const cp = char.codePointAt(0);
15750
+ if (cp == null) return false;
15751
+ return ((_a = registeredVariantCoverage.get(variantKey(family, weight, italic))) == null ? void 0 : _a.has(cp)) === true;
15752
+ };
15693
15753
  function bytesToBase64(bytes) {
15694
15754
  let binary = "";
15695
15755
  for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
@@ -15806,6 +15866,7 @@ function registerJsPdfFont(pdf, fontName, resolvedWeight, isItalic, base64) {
15806
15866
  const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, resolvedWeight, isItalic);
15807
15867
  const fileName = `${getJsPDFFontName(fontName)}-${label}${italicSuffix}.ttf`;
15808
15868
  try {
15869
+ registeredVariantCoverage.set(variantKey(fontName, resolvedWeight, isItalic), extractSupportedCodePointsFromTtf(base64ToBytes(base64)));
15809
15870
  pdf.addFileToVFS(fileName, base64);
15810
15871
  pdf.addFont(fileName, jsPdfFontName, "normal");
15811
15872
  if (fontName !== jsPdfFontName) {
@@ -15902,6 +15963,7 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
15902
15963
  if (page.elements) walkElements(page.elements);
15903
15964
  }
15904
15965
  fontKeys.add(`${FONT_FALLBACK_SYMBOLS}${SEP}400${SEP}normal`);
15966
+ fontKeys.add(`${FONT_FALLBACK_MATH}${SEP}400${SEP}normal`);
15905
15967
  for (const w of [300, 400, 500, 600, 700]) {
15906
15968
  fontKeys.add(`${FONT_FALLBACK_DEVANAGARI}${SEP}${w}${SEP}normal`);
15907
15969
  }
@@ -15935,10 +15997,10 @@ function containsDevanagari(text) {
15935
15997
  }
15936
15998
  return false;
15937
15999
  }
15938
- function containsSymbol(text) {
16000
+ function containsSymbol(text, mainSupportsChar) {
15939
16001
  if (!text) return false;
15940
16002
  for (const char of text) {
15941
- if (classifyChar(char) === "symbol") return true;
16003
+ if (classifyChar(char, mainSupportsChar) !== "main") return true;
15942
16004
  }
15943
16005
  return false;
15944
16006
  }
@@ -15952,19 +16014,25 @@ function isCommonLatinPunctuation(char) {
15952
16014
  if (c === 8467 || c === 8482) return true;
15953
16015
  return false;
15954
16016
  }
15955
- function classifyChar(char) {
16017
+ function isMathOperator(char) {
16018
+ const c = char.codePointAt(0) ?? 0;
16019
+ return c >= 8592 && c <= 8959 || c >= 8960 && c <= 9215 || c >= 10176 && c <= 11007 || c >= 11008 && c <= 11097;
16020
+ }
16021
+ function classifyChar(char, mainSupportsChar) {
15956
16022
  if (isBasicLatinOrLatin1(char)) return "main";
15957
16023
  if (isCommonLatinPunctuation(char)) return "main";
16024
+ if (!isDevanagari(char) && (mainSupportsChar == null ? void 0 : mainSupportsChar(char))) return "main";
15958
16025
  if (isDevanagari(char)) return "devanagari";
16026
+ if (isMathOperator(char)) return "math";
15959
16027
  return "symbol";
15960
16028
  }
15961
- function splitIntoRuns(text) {
16029
+ function splitIntoRuns(text, mainSupportsChar) {
15962
16030
  if (!text) return [];
15963
16031
  const runs = [];
15964
16032
  let currentType = null;
15965
16033
  let currentText = "";
15966
16034
  for (const char of text) {
15967
- const type = classifyChar(char);
16035
+ const type = classifyChar(char, mainSupportsChar);
15968
16036
  if (type !== currentType && currentText) {
15969
16037
  runs.push({ text: currentText, runType: currentType });
15970
16038
  currentText = "";
@@ -16072,15 +16140,17 @@ function rewriteSvgFontsForJsPDF(svgStr) {
16072
16140
  }
16073
16141
  const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
16074
16142
  const hasDevanagari = containsDevanagari(directText);
16075
- const hasSymbol = containsSymbol(directText);
16143
+ const mainSupportsChar = (char) => doesVariantSupportChar(clean, resolved, actualItalic, char);
16144
+ const hasSymbol = containsSymbol(directText, mainSupportsChar);
16076
16145
  if ((hasDevanagari || hasSymbol) && directText.length > 0) {
16077
16146
  const devanagariWeight = resolveFontWeight(weight);
16078
16147
  const devanagariJsPdfName = getEmbeddedJsPDFFontName(FONT_FALLBACK_DEVANAGARI, devanagariWeight);
16079
16148
  const symbolJsPdfName = isFontAvailable(FONT_FALLBACK_SYMBOLS) ? getEmbeddedJsPDFFontName(FONT_FALLBACK_SYMBOLS, 400) : jsPdfName;
16149
+ const mathJsPdfName = isFontAvailable(FONT_FALLBACK_MATH) ? getEmbeddedJsPDFFontName(FONT_FALLBACK_MATH, 400) : symbolJsPdfName;
16080
16150
  const childNodes = Array.from(el.childNodes);
16081
16151
  for (const node of childNodes) {
16082
16152
  if (node.nodeType !== 3 || !node.textContent) continue;
16083
- const runs = splitIntoRuns(node.textContent);
16153
+ const runs = splitIntoRuns(node.textContent, mainSupportsChar);
16084
16154
  if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) === "main") continue;
16085
16155
  const fragment = doc.createDocumentFragment();
16086
16156
  for (const run of runs) {
@@ -16090,6 +16160,8 @@ function rewriteSvgFontsForJsPDF(svgStr) {
16090
16160
  runFont = devanagariJsPdfName;
16091
16161
  } else if (run.runType === "symbol") {
16092
16162
  runFont = symbolJsPdfName;
16163
+ } else if (run.runType === "math") {
16164
+ runFont = mathJsPdfName;
16093
16165
  } else {
16094
16166
  runFont = jsPdfName;
16095
16167
  }
@@ -16196,6 +16268,7 @@ async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
16196
16268
  const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
16197
16269
  __proto__: null,
16198
16270
  FONT_FALLBACK_DEVANAGARI,
16271
+ FONT_FALLBACK_MATH,
16199
16272
  FONT_FALLBACK_SYMBOLS,
16200
16273
  FONT_FILES,
16201
16274
  FONT_WEIGHT_LABELS,
@@ -17672,7 +17745,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
17672
17745
  const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
17673
17746
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
17674
17747
  const shouldStripBg = stripPageBackground ?? hasGradient;
17675
- const textMode = options.textMode ?? (options.outlineText === false ? "selectable" : "pixel-perfect");
17748
+ const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
17676
17749
  const shouldOutlineText = textMode === "pixel-perfect" || textMode === "auto";
17677
17750
  const outlineSubMode = textMode === "auto" ? "complex-only" : "all";
17678
17751
  let pageSvg = page.svg;
@@ -17841,6 +17914,7 @@ function setAutoShrinkDebug(enabled) {
17841
17914
  }
17842
17915
  }
17843
17916
  exports.FONT_FALLBACK_DEVANAGARI = FONT_FALLBACK_DEVANAGARI;
17917
+ exports.FONT_FALLBACK_MATH = FONT_FALLBACK_MATH;
17844
17918
  exports.FONT_FALLBACK_SYMBOLS = FONT_FALLBACK_SYMBOLS;
17845
17919
  exports.FONT_FILES = FONT_FILES;
17846
17920
  exports.PACKAGE_VERSION = PACKAGE_VERSION;