@pixldocs/canvas-renderer 0.5.113 → 0.5.114

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.d.ts CHANGED
@@ -259,7 +259,7 @@ export declare function normalizeFontFamily(fontStack: string): string;
259
259
  * Package version banner. Bump alongside package.json so we can confirm
260
260
  * (via browser:log) that the deployed bundle matches the expected build.
261
261
  */
262
- export declare const PACKAGE_VERSION = "0.5.113";
262
+ export declare const PACKAGE_VERSION = "0.5.114";
263
263
 
264
264
  export declare interface PageSettings {
265
265
  backgroundColor?: string;
package/dist/index.js CHANGED
@@ -13998,7 +13998,7 @@ function PixldocsPreview(props) {
13998
13998
  !canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
13999
13999
  ] });
14000
14000
  }
14001
- const PACKAGE_VERSION = "0.5.113";
14001
+ const PACKAGE_VERSION = "0.5.114";
14002
14002
  const roundParityValue = (value) => {
14003
14003
  if (typeof value !== "number") return value;
14004
14004
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -15716,6 +15716,9 @@ const remoteVariantKey = (family, weight, isItalic) => `${family}|${resolveFontW
15716
15716
  const registeredVariants = /* @__PURE__ */ new Set();
15717
15717
  const registeredVariantCoverage = /* @__PURE__ */ new Map();
15718
15718
  const variantKey = (family, weight, italic) => `${family}|${resolveFontWeight(weight)}|${italic ? "i" : "n"}`;
15719
+ function isVariantEmbedded(family, weight, italic) {
15720
+ return registeredVariants.has(variantKey(family, weight, italic));
15721
+ }
15719
15722
  const resolveBestRegisteredVariant = (family, weight, italic) => {
15720
15723
  const want = resolveFontWeight(weight);
15721
15724
  const weights = [300, 400, 500, 600, 700];
@@ -16203,6 +16206,88 @@ function extractFontFamiliesFromSvgs(svgs) {
16203
16206
  }
16204
16207
  return families;
16205
16208
  }
16209
+ async function embedFontVariantsFromSvg(pdf, svgStr, fontBaseUrl) {
16210
+ var _a;
16211
+ const parser = new DOMParser();
16212
+ const doc = parser.parseFromString(svgStr, "image/svg+xml");
16213
+ const textEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
16214
+ const readStyleToken = (style, prop) => {
16215
+ var _a2;
16216
+ const m = style.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
16217
+ return ((_a2 = m == null ? void 0 : m[1]) == null ? void 0 : _a2.trim()) || null;
16218
+ };
16219
+ const resolveInherited = (el, attr, styleProp = attr) => {
16220
+ var _a2;
16221
+ let cur = el;
16222
+ while (cur) {
16223
+ const a = (_a2 = cur.getAttribute(attr)) == null ? void 0 : _a2.trim();
16224
+ if (a) return a;
16225
+ const s = readStyleToken(cur.getAttribute("style") || "", styleProp);
16226
+ if (s) return s;
16227
+ cur = cur.parentElement;
16228
+ }
16229
+ return null;
16230
+ };
16231
+ const parseWeight = (raw) => {
16232
+ const n = Number.parseInt(raw, 10);
16233
+ if (Number.isFinite(n)) return n;
16234
+ const s = raw.toLowerCase();
16235
+ if (/bold/.test(s)) return 700;
16236
+ if (/semi|demi/.test(s)) return 600;
16237
+ if (/medium/.test(s)) return 500;
16238
+ if (/light|thin/.test(s)) return 300;
16239
+ return 400;
16240
+ };
16241
+ const needed = /* @__PURE__ */ new Map();
16242
+ for (const el of textEls) {
16243
+ const rawFf = resolveInherited(el, "font-family");
16244
+ if (!rawFf) continue;
16245
+ const family = (_a = rawFf.split(",")[0]) == null ? void 0 : _a.replace(/['"]/g, "").trim();
16246
+ if (!family) continue;
16247
+ const weight = resolveFontWeight(parseWeight(resolveInherited(el, "font-weight") || "400"));
16248
+ const italic = /italic|oblique/i.test(resolveInherited(el, "font-style") || "normal");
16249
+ const key = `${family}${weight}${italic ? "i" : "n"}`;
16250
+ if (!needed.has(key)) needed.set(key, { family, weight, italic });
16251
+ }
16252
+ let embedded = 0;
16253
+ for (const { family, weight, italic } of needed.values()) {
16254
+ if (isVariantEmbedded(family, weight, italic)) continue;
16255
+ await embedFontWithGoogleFallback(pdf, family, weight, fontBaseUrl, italic);
16256
+ if (isVariantEmbedded(family, weight, italic)) embedded++;
16257
+ }
16258
+ const fallbacks = [
16259
+ { family: FONT_FALLBACK_SYMBOLS, weight: 400 },
16260
+ { family: FONT_FALLBACK_MATH, weight: 400 }
16261
+ ];
16262
+ for (const w of [300, 400, 500, 600, 700]) {
16263
+ fallbacks.push({ family: FONT_FALLBACK_DEVANAGARI, weight: w });
16264
+ }
16265
+ for (const { family, weight } of fallbacks) {
16266
+ if (isVariantEmbedded(family, weight, false)) continue;
16267
+ await embedFontWithGoogleFallback(pdf, family, weight, fontBaseUrl, false);
16268
+ if (isVariantEmbedded(family, weight, false)) embedded++;
16269
+ }
16270
+ for (const { family, weight, italic } of needed.values()) {
16271
+ if (!italic) continue;
16272
+ if (!isVariantEmbedded(family, weight, false)) {
16273
+ await embedFontWithGoogleFallback(pdf, family, weight, fontBaseUrl, false);
16274
+ if (isVariantEmbedded(family, weight, false)) embedded++;
16275
+ }
16276
+ if (!isVariantEmbedded(family, 400, false)) {
16277
+ await embedFontWithGoogleFallback(pdf, family, 400, fontBaseUrl, false);
16278
+ if (isVariantEmbedded(family, 400, false)) embedded++;
16279
+ }
16280
+ if (weight <= 400) continue;
16281
+ if (isVariantEmbedded(family, 400, true)) continue;
16282
+ await embedFontWithGoogleFallback(pdf, family, 400, fontBaseUrl, true);
16283
+ if (isVariantEmbedded(family, 400, true)) embedded++;
16284
+ }
16285
+ console.log("[pdf-fonts] embedFontVariantsFromSvg", {
16286
+ requested: needed.size,
16287
+ newlyEmbedded: embedded
16288
+ });
16289
+ return embedded;
16290
+ }
16206
16291
  async function embedFontsInPdf(pdf, fontFamilies, fontBaseUrl) {
16207
16292
  const embedded = /* @__PURE__ */ new Set();
16208
16293
  const weights = [300, 400, 500, 600, 700];
@@ -16254,6 +16339,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
16254
16339
  FONT_FILES,
16255
16340
  FONT_WEIGHT_LABELS,
16256
16341
  embedFont,
16342
+ embedFontVariantsFromSvg,
16257
16343
  embedFontWithGoogleFallback,
16258
16344
  embedFontsForConfig,
16259
16345
  embedFontsInPdf,
@@ -16261,6 +16347,7 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
16261
16347
  getEmbeddedJsPDFFontName,
16262
16348
  getFontPathForWeight,
16263
16349
  isFontAvailable,
16350
+ isVariantEmbedded,
16264
16351
  resolveBestRegisteredVariant,
16265
16352
  resolveFontWeight,
16266
16353
  rewriteSvgFontsForJsPDF
@@ -17767,7 +17854,17 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
17767
17854
  } catch {
17768
17855
  }
17769
17856
  await convertTextDecorationsToLines(processedSvg);
17770
- const rewrittenSvg = rewriteSvgFontsForJsPDF(new XMLSerializer().serializeToString(processedSvg));
17857
+ const liveSvgStr = new XMLSerializer().serializeToString(processedSvg);
17858
+ try {
17859
+ const { embedFontVariantsFromSvg: embedFontVariantsFromSvg2 } = await Promise.resolve().then(() => pdfFonts);
17860
+ await embedFontVariantsFromSvg2(pdf, liveSvgStr, fontBaseUrl);
17861
+ } catch (embedErr) {
17862
+ console.warn(
17863
+ "[canvas-renderer][pdf] embedFontVariantsFromSvg failed (page " + (i + 1) + "):",
17864
+ embedErr
17865
+ );
17866
+ }
17867
+ const rewrittenSvg = rewriteSvgFontsForJsPDF(liveSvgStr);
17771
17868
  const reParser = new DOMParser();
17772
17869
  const reDoc = reParser.parseFromString(rewrittenSvg, "image/svg+xml");
17773
17870
  processedSvg = reDoc.documentElement;