@pixldocs/canvas-renderer 0.5.129 → 0.5.132
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-Cnc7FwzD.cjs → index-4IzWx9f-.cjs} +16 -7
- package/dist/index-4IzWx9f-.cjs.map +1 -0
- package/dist/{index-BinoVoB5.js → index-TWLUgM18.js} +16 -7
- package/dist/index-TWLUgM18.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{svgTextToPath-n7TTV6Xv.js → svgTextToPath-Bne0QyE7.js} +82 -4
- package/dist/svgTextToPath-Bne0QyE7.js.map +1 -0
- package/dist/{svgTextToPath-1WbtFu9C.cjs → svgTextToPath-D6xJoPO2.cjs} +82 -4
- package/dist/svgTextToPath-D6xJoPO2.cjs.map +1 -0
- package/dist/{vectorPdfExport-DrXSoUpy.js → vectorPdfExport-3QFNDStb.js} +141 -6
- package/dist/vectorPdfExport-3QFNDStb.js.map +1 -0
- package/dist/{vectorPdfExport-Dsh0IaH2.cjs → vectorPdfExport-BmqYmqUF.cjs} +141 -6
- package/dist/vectorPdfExport-BmqYmqUF.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/index-BinoVoB5.js.map +0 -1
- package/dist/index-Cnc7FwzD.cjs.map +0 -1
- package/dist/svgTextToPath-1WbtFu9C.cjs.map +0 -1
- package/dist/svgTextToPath-n7TTV6Xv.js.map +0 -1
- package/dist/vectorPdfExport-DrXSoUpy.js.map +0 -1
- package/dist/vectorPdfExport-Dsh0IaH2.cjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsPDF, ShadingPattern } from "jspdf";
|
|
2
2
|
import { svg2pdf } from "svg2pdf.js";
|
|
3
3
|
import * as fabric from "fabric";
|
|
4
|
-
import { g as getCanvasForPage, c as captureFabricCanvasSvgForPdf, f as findNodeById, a as getAbsoluteBounds, p as parseTextMarkdown, r as renderSmartElementToSvg, n as normalizeShapeType, h as hasEdgeFade, b as getProxiedImageUrl, d as bakeEdgeFade, i as isElement, e as isGroup, j as buildRoundedTrianglePath, A as API_URL, k as getImageProxyFetchOptions, l as getRoundedRectRadii, T as TRIANGLE_STROKE_MITER_LIMIT, m as getTrianglePoints } from "./index-
|
|
4
|
+
import { g as getCanvasForPage, c as captureFabricCanvasSvgForPdf, f as findNodeById, a as getAbsoluteBounds, p as parseTextMarkdown, r as renderSmartElementToSvg, n as normalizeShapeType, h as hasEdgeFade, b as getProxiedImageUrl, d as bakeEdgeFade, i as isElement, e as isGroup, j as buildRoundedTrianglePath, A as API_URL, k as getImageProxyFetchOptions, l as getRoundedRectRadii, T as TRIANGLE_STROKE_MITER_LIMIT, m as getTrianglePoints } from "./index-TWLUgM18.js";
|
|
5
5
|
import { resetPdfFontRegistry, FONT_FALLBACK_SYMBOLS, FONT_FALLBACK_MATH, FONT_FALLBACK_DEVANAGARI, embedFontWithGoogleFallback, getEmbeddedVariantsList, isFontAvailable, isFamilyEmbedded, resolveBestRegisteredVariant, getEmbeddedJsPDFFontName, resolveFontWeight, doesVariantSupportChar } from "./pdfFonts-Y49FLHuG.js";
|
|
6
6
|
async function embedFontsForSvg(pdf, svgStr) {
|
|
7
7
|
var _a;
|
|
@@ -1078,6 +1078,112 @@ function stripSuspiciousFullPageOverlayNodes(svg) {
|
|
|
1078
1078
|
}
|
|
1079
1079
|
}
|
|
1080
1080
|
async function convertTextDecorationsToLines(svg) {
|
|
1081
|
+
return _convertTextDecorationsToLines_impl(svg);
|
|
1082
|
+
}
|
|
1083
|
+
async function bakeTextAnchorPositionsFromLiveSvg(svg) {
|
|
1084
|
+
var _a;
|
|
1085
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
1086
|
+
if (!svg) return;
|
|
1087
|
+
const _resolveAnchor = (el) => {
|
|
1088
|
+
let cur = el;
|
|
1089
|
+
while (cur) {
|
|
1090
|
+
const a = cur.getAttribute("text-anchor");
|
|
1091
|
+
if (a) return a.trim().toLowerCase();
|
|
1092
|
+
const style = cur.getAttribute("style") || "";
|
|
1093
|
+
const m = style.match(/(?:^|;)\s*text-anchor\s*:\s*([^;]+)/i);
|
|
1094
|
+
if (m) return m[1].trim().toLowerCase();
|
|
1095
|
+
cur = cur.parentElement;
|
|
1096
|
+
}
|
|
1097
|
+
return "start";
|
|
1098
|
+
};
|
|
1099
|
+
const allTextish = Array.from(svg.querySelectorAll("text, tspan"));
|
|
1100
|
+
let needsBake = false;
|
|
1101
|
+
for (const el of allTextish) {
|
|
1102
|
+
const a = _resolveAnchor(el);
|
|
1103
|
+
if (a === "middle" || a === "end") {
|
|
1104
|
+
needsBake = true;
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (!needsBake) return;
|
|
1109
|
+
try {
|
|
1110
|
+
if ((_a = document.fonts) == null ? void 0 : _a.ready) await document.fonts.ready;
|
|
1111
|
+
} catch {
|
|
1112
|
+
}
|
|
1113
|
+
const tempContainer = document.createElement("div");
|
|
1114
|
+
tempContainer.style.cssText = "position:fixed;left:-9999px;top:-9999px;visibility:hidden;pointer-events:none;";
|
|
1115
|
+
const clone = svg.cloneNode(true);
|
|
1116
|
+
for (const tn of clone.querySelectorAll("text, tspan, textPath")) {
|
|
1117
|
+
const sf = tn.getAttribute("data-source-font-family");
|
|
1118
|
+
const sw = tn.getAttribute("data-source-font-weight");
|
|
1119
|
+
const ss = tn.getAttribute("data-source-font-style");
|
|
1120
|
+
if (sf) tn.setAttribute("font-family", sf);
|
|
1121
|
+
if (sw) tn.setAttribute("font-weight", sw);
|
|
1122
|
+
if (ss) tn.setAttribute("font-style", ss);
|
|
1123
|
+
const inlineStyle = tn.getAttribute("style") || "";
|
|
1124
|
+
if (sf || sw || ss) {
|
|
1125
|
+
const stylePairs = inlineStyle.split(";").map((p) => p.trim()).filter(Boolean).filter((p) => !/^font-(family|weight|style)\s*:/i.test(p));
|
|
1126
|
+
if (sf) stylePairs.push(`font-family: ${sf}`);
|
|
1127
|
+
if (sw) stylePairs.push(`font-weight: ${sw}`);
|
|
1128
|
+
if (ss) stylePairs.push(`font-style: ${ss}`);
|
|
1129
|
+
if (stylePairs.length > 0) tn.setAttribute("style", stylePairs.join("; "));
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
tempContainer.appendChild(clone);
|
|
1133
|
+
document.body.appendChild(tempContainer);
|
|
1134
|
+
let baked = 0;
|
|
1135
|
+
try {
|
|
1136
|
+
const srcTexts = Array.from(svg.querySelectorAll("text"));
|
|
1137
|
+
const liveTexts = Array.from(clone.querySelectorAll("text"));
|
|
1138
|
+
for (let i = 0; i < srcTexts.length; i++) {
|
|
1139
|
+
const srcText = srcTexts[i];
|
|
1140
|
+
const liveText = liveTexts[i];
|
|
1141
|
+
if (!liveText) continue;
|
|
1142
|
+
const srcTspans = Array.from(srcText.querySelectorAll("tspan"));
|
|
1143
|
+
const liveTspans = Array.from(liveText.querySelectorAll("tspan"));
|
|
1144
|
+
const bakeNode = (srcNode, liveNode, isTspan) => {
|
|
1145
|
+
const anchor = _resolveAnchor(srcNode);
|
|
1146
|
+
if (anchor !== "middle" && anchor !== "end") return;
|
|
1147
|
+
try {
|
|
1148
|
+
const n = typeof liveNode.getNumberOfChars === "function" ? liveNode.getNumberOfChars() : 0;
|
|
1149
|
+
if (!n) return;
|
|
1150
|
+
const start = liveNode.getStartPositionOfChar(0);
|
|
1151
|
+
if (!Number.isFinite(start == null ? void 0 : start.x)) return;
|
|
1152
|
+
srcNode.setAttribute("x", String(start.x));
|
|
1153
|
+
srcNode.setAttribute("text-anchor", "start");
|
|
1154
|
+
const style = srcNode.getAttribute("style") || "";
|
|
1155
|
+
if (/text-anchor\s*:/i.test(style)) {
|
|
1156
|
+
const cleaned = style.split(";").map((p) => p.trim()).filter(Boolean).filter((p) => !/^text-anchor\s*:/i.test(p)).join("; ");
|
|
1157
|
+
if (cleaned) srcNode.setAttribute("style", cleaned);
|
|
1158
|
+
else srcNode.removeAttribute("style");
|
|
1159
|
+
}
|
|
1160
|
+
baked++;
|
|
1161
|
+
} catch {
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
if (srcTspans.length > 0) {
|
|
1165
|
+
bakeNode(srcText, liveText, false);
|
|
1166
|
+
for (let j = 0; j < srcTspans.length; j++) {
|
|
1167
|
+
if (liveTspans[j]) bakeNode(srcTspans[j], liveTspans[j], true);
|
|
1168
|
+
}
|
|
1169
|
+
} else {
|
|
1170
|
+
bakeNode(srcText, liveText, false);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
} finally {
|
|
1174
|
+
try {
|
|
1175
|
+
document.body.removeChild(tempContainer);
|
|
1176
|
+
} catch {
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (baked > 0) {
|
|
1180
|
+
try {
|
|
1181
|
+
console.log(`[Vector PDF][parity] baked text-anchor → start on ${baked} node(s) using live canvas measurement`);
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
async function _convertTextDecorationsToLines_impl(svg) {
|
|
1081
1187
|
const doc = svg.ownerDocument;
|
|
1082
1188
|
if (!doc) return;
|
|
1083
1189
|
const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
|
|
@@ -1495,7 +1601,7 @@ async function collectInlinedFontFaceCss() {
|
|
|
1495
1601
|
return cachedInlinedFontFaceCss;
|
|
1496
1602
|
}
|
|
1497
1603
|
async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
|
|
1498
|
-
var _a;
|
|
1604
|
+
var _a, _b;
|
|
1499
1605
|
try {
|
|
1500
1606
|
const parser = new DOMParser();
|
|
1501
1607
|
const processedSvg = inlineNestedSvgImageDataUris(rawSvg, parser);
|
|
@@ -1526,11 +1632,39 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
|
|
|
1526
1632
|
stripRootPageBackgroundFromSvg(svgToDraw);
|
|
1527
1633
|
}
|
|
1528
1634
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
1635
|
+
try {
|
|
1636
|
+
await bakeTextAnchorPositionsFromLiveSvg(svgToDraw);
|
|
1637
|
+
} catch (e) {
|
|
1638
|
+
console.warn("[Vector PDF] anchor-bake pass failed (continuing):", e);
|
|
1639
|
+
}
|
|
1640
|
+
try {
|
|
1641
|
+
const hasShadowText = !!svgToDraw.querySelector("[data-pd-shadow-blur]") || !!svgToDraw.querySelector("g.__pdShadowRaster text");
|
|
1642
|
+
if (hasShadowText) {
|
|
1643
|
+
const { convertAllTextToPath } = await import("./svgTextToPath-Bne0QyE7.js");
|
|
1644
|
+
const serialized = new XMLSerializer().serializeToString(svgToDraw);
|
|
1645
|
+
const outlined = await convertAllTextToPath(serialized, void 0, { mode: "shadow-bound" });
|
|
1646
|
+
const reparsed = parser.parseFromString(outlined, "image/svg+xml");
|
|
1647
|
+
if (!reparsed.querySelector("parsererror") && ((_a = reparsed.documentElement) == null ? void 0 : _a.tagName.toLowerCase()) === "svg") {
|
|
1648
|
+
while (svgToDraw.firstChild) svgToDraw.removeChild(svgToDraw.firstChild);
|
|
1649
|
+
for (const child of Array.from(reparsed.documentElement.childNodes)) {
|
|
1650
|
+
svgToDraw.appendChild(svgToDraw.ownerDocument.importNode(child, true));
|
|
1651
|
+
}
|
|
1652
|
+
for (const attr of Array.from(reparsed.documentElement.attributes)) {
|
|
1653
|
+
try {
|
|
1654
|
+
svgToDraw.setAttribute(attr.name, attr.value);
|
|
1655
|
+
} catch {
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
} catch (e) {
|
|
1661
|
+
console.warn("[Vector PDF] shadow-bound text outlining failed (continuing without parity outlining):", e);
|
|
1662
|
+
}
|
|
1529
1663
|
await rasterizeShadowMarkers(svgToDraw);
|
|
1530
1664
|
await convertTextDecorationsToLines(svgToDraw);
|
|
1531
1665
|
const rewritten = rewriteSvgFontsForJsPDFWithSourceMeta(new XMLSerializer().serializeToString(svgToDraw));
|
|
1532
1666
|
const rewrittenDoc = parser.parseFromString(rewritten, "image/svg+xml");
|
|
1533
|
-
if (!rewrittenDoc.querySelector("parsererror") && ((
|
|
1667
|
+
if (!rewrittenDoc.querySelector("parsererror") && ((_b = rewrittenDoc.documentElement) == null ? void 0 : _b.tagName.toLowerCase()) === "svg") {
|
|
1534
1668
|
return rewrittenDoc.documentElement;
|
|
1535
1669
|
}
|
|
1536
1670
|
return svgToDraw;
|
|
@@ -2112,7 +2246,7 @@ async function fetchSvgAsElement(imageUrl, colorMap) {
|
|
|
2112
2246
|
async function getRecoloredSvgDataUrl(imageUrl, colorMap) {
|
|
2113
2247
|
if (!colorMap || Object.keys(colorMap).length === 0) return null;
|
|
2114
2248
|
try {
|
|
2115
|
-
const { getNormalizedSvgUrl } = await import("./index-
|
|
2249
|
+
const { getNormalizedSvgUrl } = await import("./index-TWLUgM18.js").then((n) => n._);
|
|
2116
2250
|
return await getNormalizedSvgUrl(imageUrl, colorMap);
|
|
2117
2251
|
} catch {
|
|
2118
2252
|
return null;
|
|
@@ -2893,7 +3027,7 @@ async function fetchImageAsBase64(imageUrl, opts = {}) {
|
|
|
2893
3027
|
}
|
|
2894
3028
|
let fetchUrl = imageUrl;
|
|
2895
3029
|
if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
|
|
2896
|
-
const { isPrivateUrl } = await import("./index-
|
|
3030
|
+
const { isPrivateUrl } = await import("./index-TWLUgM18.js").then((n) => n._);
|
|
2897
3031
|
if (isPrivateUrl(imageUrl)) return null;
|
|
2898
3032
|
const proxyUrl = new URL(`${API_URL}/image-proxy`);
|
|
2899
3033
|
proxyUrl.searchParams.set("url", imageUrl);
|
|
@@ -4867,6 +5001,7 @@ async function exportMultiPagePdf(pages, options) {
|
|
|
4867
5001
|
pdf.save(filename);
|
|
4868
5002
|
}
|
|
4869
5003
|
export {
|
|
5004
|
+
bakeTextAnchorPositionsFromLiveSvg,
|
|
4870
5005
|
convertSvgTextDecorationsToLinesString,
|
|
4871
5006
|
drawPreparedLiveCanvasSvgPageToPdf,
|
|
4872
5007
|
embedFontsForSvg,
|
|
@@ -4875,4 +5010,4 @@ export {
|
|
|
4875
5010
|
preparePagesForExport,
|
|
4876
5011
|
rewriteSvgFontsForJsPDFWithSourceMeta
|
|
4877
5012
|
};
|
|
4878
|
-
//# sourceMappingURL=vectorPdfExport-
|
|
5013
|
+
//# sourceMappingURL=vectorPdfExport-3QFNDStb.js.map
|