@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.cjs CHANGED
@@ -5085,10 +5085,14 @@ function applyTextBackground(obj, cfg) {
5085
5085
  const blur = Math.max(0, Number(shadow.blur ?? 0));
5086
5086
  const shadowColor = String(shadow.color);
5087
5087
  const pad = Math.max(16, Math.ceil(blur * 4) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 8);
5088
- const bx = -w / 2 - pL - pad;
5089
- const by = -h / 2 - pT - pad;
5090
- const bw = w + pL + pR + pad * 2;
5091
- const bh = h + pT + pB + pad * 2;
5088
+ const shadowBounds = unionBounds([
5089
+ ...rects,
5090
+ computeTextVisualBounds(this, w, h)
5091
+ ]);
5092
+ const bx = shadowBounds.x - pad;
5093
+ const by = shadowBounds.y - pad;
5094
+ const bw = shadowBounds.w + pad * 2;
5095
+ const bh = shadowBounds.h + pad * 2;
5092
5096
  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)}"`;
5093
5097
  const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
5094
5098
  if (hasBg && (bg == null ? void 0 : bg.shadowAffectsBg) !== false) {
@@ -5150,6 +5154,51 @@ function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
5150
5154
  parts.push("Z");
5151
5155
  return parts.join(" ");
5152
5156
  }
5157
+ function unionBounds(bounds) {
5158
+ const valid = bounds.filter((b) => Number.isFinite(b.x) && Number.isFinite(b.y) && b.w > 0 && b.h > 0);
5159
+ if (valid.length === 0) return { x: 0, y: 0, w: 1, h: 1 };
5160
+ const minX = Math.min(...valid.map((b) => b.x));
5161
+ const minY = Math.min(...valid.map((b) => b.y));
5162
+ const maxX = Math.max(...valid.map((b) => b.x + b.w));
5163
+ const maxY = Math.max(...valid.map((b) => b.y + b.h));
5164
+ return { x: minX, y: minY, w: Math.max(1, maxX - minX), h: Math.max(1, maxY - minY) };
5165
+ }
5166
+ function computeTextVisualBounds(obj, w, h) {
5167
+ var _a;
5168
+ const lines = (obj == null ? void 0 : obj._textLines) ?? [];
5169
+ if (!lines || lines.length === 0) return { x: -w / 2, y: -h / 2, w, h };
5170
+ const rects = [];
5171
+ const halfW = w / 2;
5172
+ const halfH = h / 2;
5173
+ const lineHeightRatio = Math.max(0.01, Number((obj == null ? void 0 : obj.lineHeight) ?? 1) || 1);
5174
+ let cursorY = -halfH;
5175
+ for (let i = 0; i < lines.length; i++) {
5176
+ let lineW = 0;
5177
+ let lineLeft = 0;
5178
+ let lineH = 0;
5179
+ try {
5180
+ lineW = obj.getLineWidth(i) || 0;
5181
+ } catch {
5182
+ lineW = 0;
5183
+ }
5184
+ try {
5185
+ lineLeft = ((_a = obj._getLineLeftOffset) == null ? void 0 : _a.call(obj, i)) ?? 0;
5186
+ } catch {
5187
+ lineLeft = 0;
5188
+ }
5189
+ try {
5190
+ lineH = obj.getHeightOfLine(i) || 0;
5191
+ } catch {
5192
+ lineH = 0;
5193
+ }
5194
+ const rawSlotH = i === lines.length - 1 ? lineH / lineHeightRatio : lineH;
5195
+ const usedH = cursorY + halfH;
5196
+ const slotH = Math.max(0, Math.min(rawSlotH, h - usedH));
5197
+ if (lineW > 0 && slotH > 0) rects.push({ x: -halfW + lineLeft, y: cursorY, w: lineW, h: slotH });
5198
+ cursorY += slotH;
5199
+ }
5200
+ return unionBounds(rects.length > 0 ? rects : [{ x: -w / 2, y: -h / 2, w, h }]);
5201
+ }
5153
5202
  function computeBgRects(obj, w, h, pT, pR, pB, pL, fit) {
5154
5203
  var _a;
5155
5204
  if (!fit) {
@@ -13103,7 +13152,7 @@ function PixldocsPreview(props) {
13103
13152
  !canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
13104
13153
  ] });
13105
13154
  }
13106
- const PACKAGE_VERSION = "0.5.99";
13155
+ const PACKAGE_VERSION = "0.5.101";
13107
13156
  const roundParityValue = (value) => {
13108
13157
  if (typeof value !== "number") return value;
13109
13158
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -15233,6 +15282,33 @@ const GRADIENT_ATTRS_LINEAR = ["x1", "y1", "x2", "y2", "gradientUnits", "gradien
15233
15282
  const GRADIENT_ATTRS_RADIAL = ["cx", "cy", "r", "fx", "fy", "gradientUnits", "gradientTransform", "spreadMethod"];
15234
15283
  const URL_GRADIENT_RE = /^\s*url\s*\(\s*(['"]?)([^)]+?)\1\s*\)/i;
15235
15284
  const SHADOW_RASTER_ALPHA_COMPENSATION = 0.84;
15285
+ function collectFontSpecsFromMarkup(markup) {
15286
+ const specs = /* @__PURE__ */ new Set();
15287
+ const re = /<(?:text|tspan)\b[^>]*>/gi;
15288
+ let match;
15289
+ while ((match = re.exec(markup)) !== null) {
15290
+ const tag = match[0];
15291
+ const get = (attr) => {
15292
+ const m = tag.match(new RegExp(`\\s${attr}\\s*=\\s*"([^"]*)"`, "i"));
15293
+ if (m) return m[1];
15294
+ const styleM = tag.match(/\sstyle\s*=\s*"([^"]*)"/i);
15295
+ if (styleM) {
15296
+ const sm = styleM[1].match(new RegExp(`${attr}\\s*:\\s*([^;]+)`, "i"));
15297
+ if (sm) return sm[1].trim();
15298
+ }
15299
+ return null;
15300
+ };
15301
+ const family = (get("font-family") || "").split(",")[0].replace(/['"]/g, "").trim();
15302
+ if (!family) continue;
15303
+ const weight = get("font-weight") || "400";
15304
+ const style = get("font-style") || "normal";
15305
+ const size = get("font-size") || "16px";
15306
+ const sizePx = /[a-z%]/i.test(size) ? size : `${size}px`;
15307
+ const famSpec = /\s/.test(family) ? `"${family}"` : family;
15308
+ specs.add(`${style} ${weight} ${sizePx} ${famSpec}`);
15309
+ }
15310
+ return Array.from(specs);
15311
+ }
15236
15312
  function parseColor(color) {
15237
15313
  if (!color) return null;
15238
15314
  const raw = color.trim().toLowerCase();
@@ -16268,7 +16344,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
16268
16344
  }
16269
16345
  }
16270
16346
  async function rasterizeShadowMarkers(svg) {
16271
- var _a, _b, _c, _d, _e;
16347
+ var _a, _b, _c, _d, _e, _f;
16272
16348
  if (typeof window === "undefined" || typeof document === "undefined") return;
16273
16349
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
16274
16350
  if (markers.length === 0) return;
@@ -16293,6 +16369,17 @@ async function rasterizeShadowMarkers(svg) {
16293
16369
  continue;
16294
16370
  }
16295
16371
  const innerXml = Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("");
16372
+ try {
16373
+ const fontSpecs = collectFontSpecsFromMarkup(innerXml);
16374
+ if (fontSpecs.length > 0 && ((_c = document.fonts) == null ? void 0 : _c.load)) {
16375
+ await Promise.all(
16376
+ fontSpecs.map(
16377
+ (spec) => document.fonts.load(spec).catch(() => void 0)
16378
+ )
16379
+ );
16380
+ }
16381
+ } catch {
16382
+ }
16296
16383
  const scale = 2;
16297
16384
  const pxW = Math.min(4096, Math.max(8, Math.ceil(bw * scale)));
16298
16385
  const pxH = Math.min(4096, Math.max(8, Math.ceil(bh * scale)));
@@ -16302,7 +16389,7 @@ async function rasterizeShadowMarkers(svg) {
16302
16389
  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>`;
16303
16390
  const dataUrl = await rasterSvgToPngDataUrl(miniSvg, pxW, pxH);
16304
16391
  if (!dataUrl) {
16305
- (_c = marker.parentNode) == null ? void 0 : _c.removeChild(marker);
16392
+ (_d = marker.parentNode) == null ? void 0 : _d.removeChild(marker);
16306
16393
  continue;
16307
16394
  }
16308
16395
  const img = svg.ownerDocument.createElementNS(SVG_NS, "image");
@@ -16314,11 +16401,11 @@ async function rasterizeShadowMarkers(svg) {
16314
16401
  img.setAttribute("preserveAspectRatio", "none");
16315
16402
  img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
16316
16403
  img.setAttribute("href", dataUrl);
16317
- (_d = marker.parentNode) == null ? void 0 : _d.replaceChild(img, marker);
16404
+ (_e = marker.parentNode) == null ? void 0 : _e.replaceChild(img, marker);
16318
16405
  } catch (e) {
16319
16406
  console.warn("[pdf-export] rasterizeShadowMarkers failed for one marker:", e);
16320
16407
  try {
16321
- (_e = marker.parentNode) == null ? void 0 : _e.removeChild(marker);
16408
+ (_f = marker.parentNode) == null ? void 0 : _f.removeChild(marker);
16322
16409
  } catch {
16323
16410
  }
16324
16411
  }