@pixldocs/canvas-renderer 0.5.46 → 0.5.48
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 +141 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +141 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -221,7 +221,7 @@ export declare function normalizeFontFamily(fontStack: string): string;
|
|
|
221
221
|
* Package version banner. Bump alongside package.json so we can confirm
|
|
222
222
|
* (via browser:log) that the deployed bundle matches the expected build.
|
|
223
223
|
*/
|
|
224
|
-
export declare const PACKAGE_VERSION = "0.5.
|
|
224
|
+
export declare const PACKAGE_VERSION = "0.5.48";
|
|
225
225
|
|
|
226
226
|
export declare interface PageSettings {
|
|
227
227
|
backgroundColor?: string;
|
package/dist/index.js
CHANGED
|
@@ -4919,35 +4919,30 @@ function applyTextBackground(obj, cfg) {
|
|
|
4919
4919
|
const bgPath = hasBg ? `<path d="${bgD}" fill="${escapeXmlAttr(bgFill)}" />` : "";
|
|
4920
4920
|
svg = svg.replace(/style="[^"]*filter:\s*url\([^)]+\)[^"]*"/i, "");
|
|
4921
4921
|
svg = svg.replace(/<filter[\s\S]*?<\/filter>/gi, "");
|
|
4922
|
-
let
|
|
4923
|
-
let
|
|
4922
|
+
let bgShadowMarker = "";
|
|
4923
|
+
let textShadowMarker = "";
|
|
4924
4924
|
if (hasShadow) {
|
|
4925
4925
|
const ox = Number(shadow.offsetX ?? 0) || 0;
|
|
4926
4926
|
const oy = Number(shadow.offsetY ?? 0) || 0;
|
|
4927
4927
|
const blur = Math.max(0, Number(shadow.blur ?? 0));
|
|
4928
4928
|
const shadowColor = String(shadow.color);
|
|
4929
|
-
const
|
|
4929
|
+
const pad = Math.max(8, Math.ceil(blur * 3) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 4);
|
|
4930
|
+
const bx = -w / 2 - pL - pad;
|
|
4931
|
+
const by = -h / 2 - pT - pad;
|
|
4932
|
+
const bw = w + pL + pR + pad * 2;
|
|
4933
|
+
const bh = h + pT + pB + pad * 2;
|
|
4934
|
+
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)}"`;
|
|
4935
|
+
const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
|
|
4936
|
+
if (hasBg) {
|
|
4937
|
+
const shadowBgPath = `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}" />`;
|
|
4938
|
+
bgShadowMarker = wrapShadow(shadowBgPath);
|
|
4939
|
+
}
|
|
4930
4940
|
const inner = extractGInnerMarkup(svg);
|
|
4931
4941
|
const recoloredText = recolorSvgFills(inner, shadowColor);
|
|
4932
|
-
|
|
4933
|
-
const textLayers = [];
|
|
4934
|
-
const pushShadowPass = (tx, ty, opacity) => {
|
|
4935
|
-
const attrs = `transform="translate(${tx.toFixed(3)} ${ty.toFixed(3)})"${opacity ? ` opacity="${opacity}"` : ""}`;
|
|
4936
|
-
if (shadowBgPath) bgLayers.push(`<g ${attrs}>${shadowBgPath}</g>`);
|
|
4937
|
-
if (recoloredText) textLayers.push(`<g ${attrs}>${recoloredText}</g>`);
|
|
4938
|
-
};
|
|
4939
|
-
if (blur > 0) {
|
|
4940
|
-
for (const pass of buildNormalizedShadowPasses(blur)) {
|
|
4941
|
-
pushShadowPass(ox + pass.dx, oy + pass.dy, pass.opacity);
|
|
4942
|
-
}
|
|
4943
|
-
} else {
|
|
4944
|
-
pushShadowPass(ox, oy);
|
|
4945
|
-
}
|
|
4946
|
-
bgShadowLayer = bgLayers.join("");
|
|
4947
|
-
textShadowLayer = textLayers.join("");
|
|
4942
|
+
if (recoloredText) textShadowMarker = wrapShadow(recoloredText);
|
|
4948
4943
|
}
|
|
4949
4944
|
const openTagMatch = svg.match(/^\s*<g\b[^>]*>/);
|
|
4950
|
-
const inserted =
|
|
4945
|
+
const inserted = bgShadowMarker + bgPath + textShadowMarker;
|
|
4951
4946
|
if (openTagMatch) {
|
|
4952
4947
|
const openTag = openTagMatch[0];
|
|
4953
4948
|
return svg.replace(openTag, openTag + inserted);
|
|
@@ -4975,36 +4970,6 @@ function recolorSvgFills(svg, color) {
|
|
|
4975
4970
|
);
|
|
4976
4971
|
return out;
|
|
4977
4972
|
}
|
|
4978
|
-
function buildNormalizedShadowPasses(blur) {
|
|
4979
|
-
const safeBlur = Math.max(0, Number(blur) || 0);
|
|
4980
|
-
const ringCount = Math.min(5, Math.max(2, Math.round(safeBlur / 5)));
|
|
4981
|
-
const targetOpacity = Math.max(0.16, Math.min(0.38, 0.46 - safeBlur * 0.01));
|
|
4982
|
-
const weighted = [
|
|
4983
|
-
{ dx: 0, dy: 0, weight: 0.7 }
|
|
4984
|
-
];
|
|
4985
|
-
for (let i = 1; i <= ringCount; i++) {
|
|
4986
|
-
const t = i / ringCount;
|
|
4987
|
-
const dist = safeBlur * t * 0.42;
|
|
4988
|
-
const weight = Math.exp(-2.4 * t * t);
|
|
4989
|
-
const diag = dist * 0.7071;
|
|
4990
|
-
weighted.push(
|
|
4991
|
-
{ dx: dist, dy: 0, weight },
|
|
4992
|
-
{ dx: -dist, dy: 0, weight },
|
|
4993
|
-
{ dx: 0, dy: dist, weight },
|
|
4994
|
-
{ dx: 0, dy: -dist, weight },
|
|
4995
|
-
{ dx: diag, dy: diag, weight: weight * 0.72 },
|
|
4996
|
-
{ dx: -diag, dy: diag, weight: weight * 0.72 },
|
|
4997
|
-
{ dx: diag, dy: -diag, weight: weight * 0.72 },
|
|
4998
|
-
{ dx: -diag, dy: -diag, weight: weight * 0.72 }
|
|
4999
|
-
);
|
|
5000
|
-
}
|
|
5001
|
-
const totalWeight = weighted.reduce((sum, pass) => sum + pass.weight, 0) || 1;
|
|
5002
|
-
return weighted.map((pass) => ({
|
|
5003
|
-
dx: pass.dx,
|
|
5004
|
-
dy: pass.dy,
|
|
5005
|
-
opacity: Math.max(3e-3, targetOpacity * pass.weight / totalWeight).toFixed(4)
|
|
5006
|
-
}));
|
|
5007
|
-
}
|
|
5008
4973
|
function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
|
|
5009
4974
|
const maxR = Math.min(w, h) / 2;
|
|
5010
4975
|
const tl = Math.min(Math.max(0, rTL), maxR);
|
|
@@ -12356,7 +12321,7 @@ function PixldocsPreview(props) {
|
|
|
12356
12321
|
!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..." }) })
|
|
12357
12322
|
] });
|
|
12358
12323
|
}
|
|
12359
|
-
const PACKAGE_VERSION = "0.5.
|
|
12324
|
+
const PACKAGE_VERSION = "0.5.48";
|
|
12360
12325
|
let __underlineFixInstalled = false;
|
|
12361
12326
|
function installUnderlineFix(fab) {
|
|
12362
12327
|
var _a;
|
|
@@ -15022,6 +14987,130 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
15022
14987
|
}
|
|
15023
14988
|
}
|
|
15024
14989
|
}
|
|
14990
|
+
async function rasterizeShadowMarkers(svg) {
|
|
14991
|
+
var _a, _b, _c, _d, _e;
|
|
14992
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
14993
|
+
const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
|
|
14994
|
+
if (markers.length === 0) return;
|
|
14995
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
14996
|
+
const XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
14997
|
+
try {
|
|
14998
|
+
if ((_a = document.fonts) == null ? void 0 : _a.ready) await document.fonts.ready;
|
|
14999
|
+
} catch {
|
|
15000
|
+
}
|
|
15001
|
+
const fontFaceCss = collectDocumentFontFaceCss();
|
|
15002
|
+
for (const marker of markers) {
|
|
15003
|
+
try {
|
|
15004
|
+
const blur = parseFloat(marker.getAttribute("data-blur") || "0");
|
|
15005
|
+
const ox = parseFloat(marker.getAttribute("data-ox") || "0");
|
|
15006
|
+
const oy = parseFloat(marker.getAttribute("data-oy") || "0");
|
|
15007
|
+
const bx = parseFloat(marker.getAttribute("data-bx") || "0");
|
|
15008
|
+
const by = parseFloat(marker.getAttribute("data-by") || "0");
|
|
15009
|
+
const bw = parseFloat(marker.getAttribute("data-bw") || "0");
|
|
15010
|
+
const bh = parseFloat(marker.getAttribute("data-bh") || "0");
|
|
15011
|
+
if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
|
|
15012
|
+
(_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
|
|
15013
|
+
continue;
|
|
15014
|
+
}
|
|
15015
|
+
const innerXml = Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("");
|
|
15016
|
+
const scale = 2;
|
|
15017
|
+
const pxW = Math.min(4096, Math.max(8, Math.ceil(bw * scale)));
|
|
15018
|
+
const pxH = Math.min(4096, Math.max(8, Math.ceil(bh * scale)));
|
|
15019
|
+
const stdDev = Math.max(0, blur);
|
|
15020
|
+
const filterId = `pdShadowBlur_${Math.random().toString(36).slice(2, 9)}`;
|
|
15021
|
+
const styleBlock = fontFaceCss ? `<style>${fontFaceCss}</style>` : "";
|
|
15022
|
+
const miniSvg = `<svg xmlns="${SVG_NS}" xmlns:xlink="${XLINK_NS}" width="${pxW}" height="${pxH}" viewBox="${bx} ${by} ${bw} ${bh}"><defs>${styleBlock}<filter id="${filterId}" x="-50%" y="-50%" width="200%" height="200%" 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>`;
|
|
15023
|
+
const dataUrl = await rasterSvgToPngDataUrl(miniSvg, pxW, pxH);
|
|
15024
|
+
if (!dataUrl) {
|
|
15025
|
+
(_c = marker.parentNode) == null ? void 0 : _c.removeChild(marker);
|
|
15026
|
+
continue;
|
|
15027
|
+
}
|
|
15028
|
+
const img = svg.ownerDocument.createElementNS(SVG_NS, "image");
|
|
15029
|
+
img.setAttribute("x", String(bx));
|
|
15030
|
+
img.setAttribute("y", String(by));
|
|
15031
|
+
img.setAttribute("width", String(bw));
|
|
15032
|
+
img.setAttribute("height", String(bh));
|
|
15033
|
+
img.setAttribute("preserveAspectRatio", "none");
|
|
15034
|
+
img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
|
|
15035
|
+
img.setAttribute("href", dataUrl);
|
|
15036
|
+
(_d = marker.parentNode) == null ? void 0 : _d.replaceChild(img, marker);
|
|
15037
|
+
} catch (e) {
|
|
15038
|
+
console.warn("[pdf-export] rasterizeShadowMarkers failed for one marker:", e);
|
|
15039
|
+
try {
|
|
15040
|
+
(_e = marker.parentNode) == null ? void 0 : _e.removeChild(marker);
|
|
15041
|
+
} catch {
|
|
15042
|
+
}
|
|
15043
|
+
}
|
|
15044
|
+
}
|
|
15045
|
+
}
|
|
15046
|
+
function rasterSvgToPngDataUrl(svgMarkup, pxW, pxH) {
|
|
15047
|
+
return new Promise((resolve) => {
|
|
15048
|
+
try {
|
|
15049
|
+
const blob = new Blob([svgMarkup], { type: "image/svg+xml;charset=utf-8" });
|
|
15050
|
+
const url = URL.createObjectURL(blob);
|
|
15051
|
+
const img = new Image();
|
|
15052
|
+
img.crossOrigin = "anonymous";
|
|
15053
|
+
const cleanup = () => {
|
|
15054
|
+
try {
|
|
15055
|
+
URL.revokeObjectURL(url);
|
|
15056
|
+
} catch {
|
|
15057
|
+
}
|
|
15058
|
+
};
|
|
15059
|
+
img.onload = () => {
|
|
15060
|
+
try {
|
|
15061
|
+
const canvas = document.createElement("canvas");
|
|
15062
|
+
canvas.width = pxW;
|
|
15063
|
+
canvas.height = pxH;
|
|
15064
|
+
const ctx = canvas.getContext("2d");
|
|
15065
|
+
if (!ctx) {
|
|
15066
|
+
cleanup();
|
|
15067
|
+
resolve(null);
|
|
15068
|
+
return;
|
|
15069
|
+
}
|
|
15070
|
+
ctx.drawImage(img, 0, 0, pxW, pxH);
|
|
15071
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
15072
|
+
cleanup();
|
|
15073
|
+
resolve(dataUrl);
|
|
15074
|
+
} catch (e) {
|
|
15075
|
+
cleanup();
|
|
15076
|
+
resolve(null);
|
|
15077
|
+
}
|
|
15078
|
+
};
|
|
15079
|
+
img.onerror = () => {
|
|
15080
|
+
cleanup();
|
|
15081
|
+
resolve(null);
|
|
15082
|
+
};
|
|
15083
|
+
img.src = url;
|
|
15084
|
+
} catch {
|
|
15085
|
+
resolve(null);
|
|
15086
|
+
}
|
|
15087
|
+
});
|
|
15088
|
+
}
|
|
15089
|
+
let cachedFontFaceCss = null;
|
|
15090
|
+
function collectDocumentFontFaceCss() {
|
|
15091
|
+
if (cachedFontFaceCss !== null) return cachedFontFaceCss;
|
|
15092
|
+
const out = [];
|
|
15093
|
+
try {
|
|
15094
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
15095
|
+
let rules = null;
|
|
15096
|
+
try {
|
|
15097
|
+
rules = sheet.cssRules;
|
|
15098
|
+
} catch {
|
|
15099
|
+
rules = null;
|
|
15100
|
+
}
|
|
15101
|
+
if (!rules) continue;
|
|
15102
|
+
for (const rule of Array.from(rules)) {
|
|
15103
|
+
const r = rule;
|
|
15104
|
+
if (r && (r.type === 5 || /@font-face/i.test(r.cssText || ""))) {
|
|
15105
|
+
if (r.cssText) out.push(r.cssText);
|
|
15106
|
+
}
|
|
15107
|
+
}
|
|
15108
|
+
}
|
|
15109
|
+
} catch {
|
|
15110
|
+
}
|
|
15111
|
+
cachedFontFaceCss = out.join("\n");
|
|
15112
|
+
return cachedFontFaceCss;
|
|
15113
|
+
}
|
|
15025
15114
|
async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
|
|
15026
15115
|
try {
|
|
15027
15116
|
const parser = new DOMParser();
|
|
@@ -15051,6 +15140,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
|
|
|
15051
15140
|
stripSuspiciousFullPageOverlayNodes(svgToDraw);
|
|
15052
15141
|
if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
|
|
15053
15142
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
15143
|
+
await rasterizeShadowMarkers(svgToDraw);
|
|
15054
15144
|
return svgToDraw;
|
|
15055
15145
|
} catch {
|
|
15056
15146
|
return null;
|