@pixldocs/canvas-renderer 0.5.99 → 0.5.101

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
@@ -256,7 +256,7 @@ export declare function normalizeFontFamily(fontStack: string): string;
256
256
  * Package version banner. Bump alongside package.json so we can confirm
257
257
  * (via browser:log) that the deployed bundle matches the expected build.
258
258
  */
259
- export declare const PACKAGE_VERSION = "0.5.99";
259
+ export declare const PACKAGE_VERSION = "0.5.101";
260
260
 
261
261
  export declare interface PageSettings {
262
262
  backgroundColor?: string;
package/dist/index.js CHANGED
@@ -5066,10 +5066,14 @@ function applyTextBackground(obj, cfg) {
5066
5066
  const blur = Math.max(0, Number(shadow.blur ?? 0));
5067
5067
  const shadowColor = String(shadow.color);
5068
5068
  const pad = Math.max(16, Math.ceil(blur * 4) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 8);
5069
- const bx = -w / 2 - pL - pad;
5070
- const by = -h / 2 - pT - pad;
5071
- const bw = w + pL + pR + pad * 2;
5072
- const bh = h + pT + pB + pad * 2;
5069
+ const shadowBounds = unionBounds([
5070
+ ...rects,
5071
+ computeTextVisualBounds(this, w, h)
5072
+ ]);
5073
+ const bx = shadowBounds.x - pad;
5074
+ const by = shadowBounds.y - pad;
5075
+ const bw = shadowBounds.w + pad * 2;
5076
+ const bh = shadowBounds.h + pad * 2;
5073
5077
  const dataAttrs = `data-blur="${blur.toFixed(3)}" data-ox="${ox.toFixed(3)}" data-oy="${oy.toFixed(3)}" data-bx="${bx.toFixed(3)}" data-by="${by.toFixed(3)}" data-bw="${bw.toFixed(3)}" data-bh="${bh.toFixed(3)}" data-color="${escapeXmlAttr(shadowColor)}"`;
5074
5078
  const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
5075
5079
  if (hasBg && (bg == null ? void 0 : bg.shadowAffectsBg) !== false) {
@@ -5131,6 +5135,51 @@ function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
5131
5135
  parts.push("Z");
5132
5136
  return parts.join(" ");
5133
5137
  }
5138
+ function unionBounds(bounds) {
5139
+ const valid = bounds.filter((b) => Number.isFinite(b.x) && Number.isFinite(b.y) && b.w > 0 && b.h > 0);
5140
+ if (valid.length === 0) return { x: 0, y: 0, w: 1, h: 1 };
5141
+ const minX = Math.min(...valid.map((b) => b.x));
5142
+ const minY = Math.min(...valid.map((b) => b.y));
5143
+ const maxX = Math.max(...valid.map((b) => b.x + b.w));
5144
+ const maxY = Math.max(...valid.map((b) => b.y + b.h));
5145
+ return { x: minX, y: minY, w: Math.max(1, maxX - minX), h: Math.max(1, maxY - minY) };
5146
+ }
5147
+ function computeTextVisualBounds(obj, w, h) {
5148
+ var _a;
5149
+ const lines = (obj == null ? void 0 : obj._textLines) ?? [];
5150
+ if (!lines || lines.length === 0) return { x: -w / 2, y: -h / 2, w, h };
5151
+ const rects = [];
5152
+ const halfW = w / 2;
5153
+ const halfH = h / 2;
5154
+ const lineHeightRatio = Math.max(0.01, Number((obj == null ? void 0 : obj.lineHeight) ?? 1) || 1);
5155
+ let cursorY = -halfH;
5156
+ for (let i = 0; i < lines.length; i++) {
5157
+ let lineW = 0;
5158
+ let lineLeft = 0;
5159
+ let lineH = 0;
5160
+ try {
5161
+ lineW = obj.getLineWidth(i) || 0;
5162
+ } catch {
5163
+ lineW = 0;
5164
+ }
5165
+ try {
5166
+ lineLeft = ((_a = obj._getLineLeftOffset) == null ? void 0 : _a.call(obj, i)) ?? 0;
5167
+ } catch {
5168
+ lineLeft = 0;
5169
+ }
5170
+ try {
5171
+ lineH = obj.getHeightOfLine(i) || 0;
5172
+ } catch {
5173
+ lineH = 0;
5174
+ }
5175
+ const rawSlotH = i === lines.length - 1 ? lineH / lineHeightRatio : lineH;
5176
+ const usedH = cursorY + halfH;
5177
+ const slotH = Math.max(0, Math.min(rawSlotH, h - usedH));
5178
+ if (lineW > 0 && slotH > 0) rects.push({ x: -halfW + lineLeft, y: cursorY, w: lineW, h: slotH });
5179
+ cursorY += slotH;
5180
+ }
5181
+ return unionBounds(rects.length > 0 ? rects : [{ x: -w / 2, y: -h / 2, w, h }]);
5182
+ }
5134
5183
  function computeBgRects(obj, w, h, pT, pR, pB, pL, fit) {
5135
5184
  var _a;
5136
5185
  if (!fit) {
@@ -13084,7 +13133,7 @@ function PixldocsPreview(props) {
13084
13133
  !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..." }) })
13085
13134
  ] });
13086
13135
  }
13087
- const PACKAGE_VERSION = "0.5.99";
13136
+ const PACKAGE_VERSION = "0.5.101";
13088
13137
  const roundParityValue = (value) => {
13089
13138
  if (typeof value !== "number") return value;
13090
13139
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -15214,6 +15263,33 @@ const GRADIENT_ATTRS_LINEAR = ["x1", "y1", "x2", "y2", "gradientUnits", "gradien
15214
15263
  const GRADIENT_ATTRS_RADIAL = ["cx", "cy", "r", "fx", "fy", "gradientUnits", "gradientTransform", "spreadMethod"];
15215
15264
  const URL_GRADIENT_RE = /^\s*url\s*\(\s*(['"]?)([^)]+?)\1\s*\)/i;
15216
15265
  const SHADOW_RASTER_ALPHA_COMPENSATION = 0.84;
15266
+ function collectFontSpecsFromMarkup(markup) {
15267
+ const specs = /* @__PURE__ */ new Set();
15268
+ const re = /<(?:text|tspan)\b[^>]*>/gi;
15269
+ let match;
15270
+ while ((match = re.exec(markup)) !== null) {
15271
+ const tag = match[0];
15272
+ const get = (attr) => {
15273
+ const m = tag.match(new RegExp(`\\s${attr}\\s*=\\s*"([^"]*)"`, "i"));
15274
+ if (m) return m[1];
15275
+ const styleM = tag.match(/\sstyle\s*=\s*"([^"]*)"/i);
15276
+ if (styleM) {
15277
+ const sm = styleM[1].match(new RegExp(`${attr}\\s*:\\s*([^;]+)`, "i"));
15278
+ if (sm) return sm[1].trim();
15279
+ }
15280
+ return null;
15281
+ };
15282
+ const family = (get("font-family") || "").split(",")[0].replace(/['"]/g, "").trim();
15283
+ if (!family) continue;
15284
+ const weight = get("font-weight") || "400";
15285
+ const style = get("font-style") || "normal";
15286
+ const size = get("font-size") || "16px";
15287
+ const sizePx = /[a-z%]/i.test(size) ? size : `${size}px`;
15288
+ const famSpec = /\s/.test(family) ? `"${family}"` : family;
15289
+ specs.add(`${style} ${weight} ${sizePx} ${famSpec}`);
15290
+ }
15291
+ return Array.from(specs);
15292
+ }
15217
15293
  function parseColor(color) {
15218
15294
  if (!color) return null;
15219
15295
  const raw = color.trim().toLowerCase();
@@ -16249,7 +16325,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
16249
16325
  }
16250
16326
  }
16251
16327
  async function rasterizeShadowMarkers(svg) {
16252
- var _a, _b, _c, _d, _e;
16328
+ var _a, _b, _c, _d, _e, _f;
16253
16329
  if (typeof window === "undefined" || typeof document === "undefined") return;
16254
16330
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
16255
16331
  if (markers.length === 0) return;
@@ -16274,6 +16350,17 @@ async function rasterizeShadowMarkers(svg) {
16274
16350
  continue;
16275
16351
  }
16276
16352
  const innerXml = Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("");
16353
+ try {
16354
+ const fontSpecs = collectFontSpecsFromMarkup(innerXml);
16355
+ if (fontSpecs.length > 0 && ((_c = document.fonts) == null ? void 0 : _c.load)) {
16356
+ await Promise.all(
16357
+ fontSpecs.map(
16358
+ (spec) => document.fonts.load(spec).catch(() => void 0)
16359
+ )
16360
+ );
16361
+ }
16362
+ } catch {
16363
+ }
16277
16364
  const scale = 2;
16278
16365
  const pxW = Math.min(4096, Math.max(8, Math.ceil(bw * scale)));
16279
16366
  const pxH = Math.min(4096, Math.max(8, Math.ceil(bh * scale)));
@@ -16283,7 +16370,7 @@ async function rasterizeShadowMarkers(svg) {
16283
16370
  const miniSvg = `<svg xmlns="${SVG_NS}" xmlns:xlink="${XLINK_NS}" width="${pxW}" height="${pxH}" viewBox="${bx} ${by} ${bw} ${bh}">${styleBlock}<defs><filter id="${filterId}" filterUnits="userSpaceOnUse" x="${bx}" y="${by}" width="${bw}" height="${bh}" color-interpolation-filters="sRGB"><feOffset dx="${ox}" dy="${oy}" result="offsetShadow" /><feGaussianBlur in="offsetShadow" stdDeviation="${stdDev}" /></filter></defs><g filter="url(#${filterId})">${innerXml}</g></svg>`;
16284
16371
  const dataUrl = await rasterSvgToPngDataUrl(miniSvg, pxW, pxH);
16285
16372
  if (!dataUrl) {
16286
- (_c = marker.parentNode) == null ? void 0 : _c.removeChild(marker);
16373
+ (_d = marker.parentNode) == null ? void 0 : _d.removeChild(marker);
16287
16374
  continue;
16288
16375
  }
16289
16376
  const img = svg.ownerDocument.createElementNS(SVG_NS, "image");
@@ -16295,11 +16382,11 @@ async function rasterizeShadowMarkers(svg) {
16295
16382
  img.setAttribute("preserveAspectRatio", "none");
16296
16383
  img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
16297
16384
  img.setAttribute("href", dataUrl);
16298
- (_d = marker.parentNode) == null ? void 0 : _d.replaceChild(img, marker);
16385
+ (_e = marker.parentNode) == null ? void 0 : _e.replaceChild(img, marker);
16299
16386
  } catch (e) {
16300
16387
  console.warn("[pdf-export] rasterizeShadowMarkers failed for one marker:", e);
16301
16388
  try {
16302
- (_e = marker.parentNode) == null ? void 0 : _e.removeChild(marker);
16389
+ (_f = marker.parentNode) == null ? void 0 : _f.removeChild(marker);
16303
16390
  } catch {
16304
16391
  }
16305
16392
  }