@pixldocs/canvas-renderer 0.5.25 → 0.5.27
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 +227 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +227 -197
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9613,6 +9613,10 @@ function PreviewCanvas({
|
|
|
9613
9613
|
}
|
|
9614
9614
|
);
|
|
9615
9615
|
}
|
|
9616
|
+
const PreviewCanvas$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
9617
|
+
__proto__: null,
|
|
9618
|
+
PreviewCanvas
|
|
9619
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
9616
9620
|
function applyThemeToConfig(config, themeOverrides) {
|
|
9617
9621
|
var _a, _b, _c;
|
|
9618
9622
|
if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;
|
|
@@ -11829,74 +11833,17 @@ async function ensureFontsForResolvedConfig(config) {
|
|
|
11829
11833
|
if (typeof document === "undefined") return;
|
|
11830
11834
|
const descriptors = collectFontDescriptorsFromConfig(config);
|
|
11831
11835
|
const families = new Set(descriptors.map((d) => d.family));
|
|
11832
|
-
|
|
11836
|
+
void withTimeout(Promise.all([...families].map((f) => loadGoogleFontCSS(f))), 2500);
|
|
11833
11837
|
if (document.fonts) {
|
|
11834
|
-
|
|
11838
|
+
descriptors.forEach((d) => {
|
|
11835
11839
|
const stylePrefix = d.style === "italic" ? "italic " : "";
|
|
11836
11840
|
const weightStr = String(d.weight);
|
|
11837
11841
|
const spec = `${stylePrefix}${weightStr} 16px "${d.family}"`;
|
|
11838
|
-
|
|
11842
|
+
document.fonts.load(spec).catch(() => {
|
|
11839
11843
|
});
|
|
11840
|
-
})
|
|
11841
|
-
await withTimeout(document.fonts.ready, 6e3);
|
|
11844
|
+
});
|
|
11842
11845
|
}
|
|
11843
11846
|
}
|
|
11844
|
-
const TEXT_TYPES = /* @__PURE__ */ new Set(["textbox", "text", "i-text"]);
|
|
11845
|
-
function getFabricCanvasFromContainer(container) {
|
|
11846
|
-
const registry2 = window.__fabricCanvasRegistry;
|
|
11847
|
-
if (registry2 instanceof Map) {
|
|
11848
|
-
for (const entry of registry2.values()) {
|
|
11849
|
-
const canvas = (entry == null ? void 0 : entry.canvas) || entry;
|
|
11850
|
-
if (!canvas || typeof canvas.toSVG !== "function") continue;
|
|
11851
|
-
const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
|
|
11852
|
-
if (el && container.contains(el)) return canvas;
|
|
11853
|
-
}
|
|
11854
|
-
}
|
|
11855
|
-
return null;
|
|
11856
|
-
}
|
|
11857
|
-
function stabilizeFabricTextObjects(fabricInstance) {
|
|
11858
|
-
var _a, _b, _c;
|
|
11859
|
-
if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
|
|
11860
|
-
clearFabricCharCache();
|
|
11861
|
-
clearMeasurementCache();
|
|
11862
|
-
const walk = (obj) => {
|
|
11863
|
-
var _a2, _b2, _c2, _d;
|
|
11864
|
-
if (!obj) return;
|
|
11865
|
-
const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
|
|
11866
|
-
if (children.length) children.forEach(walk);
|
|
11867
|
-
const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (TEXT_TYPES.has(obj.type) || obj.isEditing !== void 0);
|
|
11868
|
-
if (!isTextObject) return;
|
|
11869
|
-
const saved = { width: obj.width, scaleX: obj.scaleX, scaleY: obj.scaleY };
|
|
11870
|
-
const reset = () => {
|
|
11871
|
-
var _a3;
|
|
11872
|
-
(_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
|
|
11873
|
-
obj.__charBounds = [];
|
|
11874
|
-
obj.__lineWidths = [];
|
|
11875
|
-
obj.__lineHeights = [];
|
|
11876
|
-
obj.__graphemeLines = [];
|
|
11877
|
-
obj._textLines = [];
|
|
11878
|
-
obj.textLines = [];
|
|
11879
|
-
obj._styleMap = null;
|
|
11880
|
-
obj.styleMap = null;
|
|
11881
|
-
obj.dirty = true;
|
|
11882
|
-
};
|
|
11883
|
-
reset();
|
|
11884
|
-
obj.initDimensions();
|
|
11885
|
-
if (saved.width != null) {
|
|
11886
|
-
(_a2 = obj.set) == null ? void 0 : _a2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
|
|
11887
|
-
reset();
|
|
11888
|
-
obj.initDimensions();
|
|
11889
|
-
(_b2 = obj.set) == null ? void 0 : _b2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
|
|
11890
|
-
}
|
|
11891
|
-
obj.dirty = true;
|
|
11892
|
-
(_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
|
|
11893
|
-
(_d = obj.setCoords) == null ? void 0 : _d.call(obj);
|
|
11894
|
-
};
|
|
11895
|
-
fabricInstance.getObjects().forEach(walk);
|
|
11896
|
-
(_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
|
|
11897
|
-
(_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
|
|
11898
|
-
(_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
|
|
11899
|
-
}
|
|
11900
11847
|
function PixldocsPreview(props) {
|
|
11901
11848
|
const {
|
|
11902
11849
|
pageIndex = 0,
|
|
@@ -11907,7 +11854,8 @@ function PixldocsPreview(props) {
|
|
|
11907
11854
|
style,
|
|
11908
11855
|
onDynamicFieldClick,
|
|
11909
11856
|
onReady,
|
|
11910
|
-
onError
|
|
11857
|
+
onError,
|
|
11858
|
+
skipFontReadyWait = true
|
|
11911
11859
|
} = props;
|
|
11912
11860
|
useEffect(() => {
|
|
11913
11861
|
setPackageApiUrl(imageProxyUrl);
|
|
@@ -11980,6 +11928,7 @@ function PixldocsPreview(props) {
|
|
|
11980
11928
|
const bump = () => {
|
|
11981
11929
|
if (cancelled) return;
|
|
11982
11930
|
clearMeasurementCache();
|
|
11931
|
+
clearFabricCharCache();
|
|
11983
11932
|
setFontsReadyVersion((v) => v + 1);
|
|
11984
11933
|
};
|
|
11985
11934
|
(_b = (_a = document.fonts) == null ? void 0 : _a.ready) == null ? void 0 : _b.then(bump);
|
|
@@ -11993,13 +11942,6 @@ function PixldocsPreview(props) {
|
|
|
11993
11942
|
() => `${pageIndex}-${fontsReadyVersion}-${stabilizationPass}`,
|
|
11994
11943
|
[pageIndex, fontsReadyVersion, stabilizationPass]
|
|
11995
11944
|
);
|
|
11996
|
-
const previewWrapRef = useCallback((node) => {
|
|
11997
|
-
if (!node || !config) return;
|
|
11998
|
-
requestAnimationFrame(() => {
|
|
11999
|
-
const fabricCanvas = getFabricCanvasFromContainer(node);
|
|
12000
|
-
if (fabricCanvas) stabilizeFabricTextObjects(fabricCanvas);
|
|
12001
|
-
});
|
|
12002
|
-
}, [config, previewKey]);
|
|
12003
11945
|
useEffect(() => {
|
|
12004
11946
|
if (isResolveMode) return;
|
|
12005
11947
|
if (!config) {
|
|
@@ -12030,13 +11972,14 @@ function PixldocsPreview(props) {
|
|
|
12030
11972
|
return /* @__PURE__ */ jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
|
|
12031
11973
|
}
|
|
12032
11974
|
return /* @__PURE__ */ jsxs("div", { className, style: { ...style, position: "relative" }, children: [
|
|
12033
|
-
/* @__PURE__ */ jsx("div", {
|
|
11975
|
+
/* @__PURE__ */ jsx("div", { style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsx(
|
|
12034
11976
|
PreviewCanvas,
|
|
12035
11977
|
{
|
|
12036
11978
|
config,
|
|
12037
11979
|
pageIndex,
|
|
12038
11980
|
zoom,
|
|
12039
11981
|
absoluteZoom,
|
|
11982
|
+
skipFontReadyWait,
|
|
12040
11983
|
onDynamicFieldClick,
|
|
12041
11984
|
onReady: handleCanvasReady
|
|
12042
11985
|
},
|
|
@@ -12045,70 +11988,6 @@ function PixldocsPreview(props) {
|
|
|
12045
11988
|
!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..." }) })
|
|
12046
11989
|
] });
|
|
12047
11990
|
}
|
|
12048
|
-
const PixldocsPreview$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
12049
|
-
__proto__: null,
|
|
12050
|
-
PixldocsPreview
|
|
12051
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
12052
|
-
const inlinedAssetCache = /* @__PURE__ */ new Map();
|
|
12053
|
-
function shouldInlineImageUrl(url) {
|
|
12054
|
-
if (!url || url.startsWith("data:")) return false;
|
|
12055
|
-
if (url.startsWith("blob:")) return true;
|
|
12056
|
-
if (url.startsWith("/") && !url.startsWith("//")) return true;
|
|
12057
|
-
try {
|
|
12058
|
-
const parsed = new URL(url, window.location.href);
|
|
12059
|
-
const current = new URL(window.location.href);
|
|
12060
|
-
const host = parsed.hostname.toLowerCase();
|
|
12061
|
-
return parsed.origin === current.origin || host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" || host.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(host);
|
|
12062
|
-
} catch {
|
|
12063
|
-
return false;
|
|
12064
|
-
}
|
|
12065
|
-
}
|
|
12066
|
-
async function imageUrlToDataUrl(url) {
|
|
12067
|
-
if (inlinedAssetCache.has(url)) return inlinedAssetCache.get(url) ?? null;
|
|
12068
|
-
try {
|
|
12069
|
-
const response = await fetch(url);
|
|
12070
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
12071
|
-
const blob = await response.blob();
|
|
12072
|
-
if (blob.type.includes("text/html")) throw new Error("Expected image but got text/html");
|
|
12073
|
-
const dataUrl = await new Promise((resolve, reject) => {
|
|
12074
|
-
const reader = new FileReader();
|
|
12075
|
-
reader.onloadend = () => resolve(String(reader.result || ""));
|
|
12076
|
-
reader.onerror = reject;
|
|
12077
|
-
reader.readAsDataURL(blob);
|
|
12078
|
-
});
|
|
12079
|
-
inlinedAssetCache.set(url, dataUrl);
|
|
12080
|
-
return dataUrl;
|
|
12081
|
-
} catch (error) {
|
|
12082
|
-
console.warn("[@pixldocs/canvas-renderer] Failed to inline image asset:", url, error);
|
|
12083
|
-
inlinedAssetCache.set(url, null);
|
|
12084
|
-
return null;
|
|
12085
|
-
}
|
|
12086
|
-
}
|
|
12087
|
-
async function inlineNodeAssets(node) {
|
|
12088
|
-
if (node.type === "image") {
|
|
12089
|
-
const url = typeof node.src === "string" && node.src.trim() ? node.src.trim() : typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
|
|
12090
|
-
if (shouldInlineImageUrl(url)) {
|
|
12091
|
-
const dataUrl = await imageUrlToDataUrl(url);
|
|
12092
|
-
if (dataUrl) {
|
|
12093
|
-
node.src = dataUrl;
|
|
12094
|
-
node.imageUrl = dataUrl;
|
|
12095
|
-
}
|
|
12096
|
-
}
|
|
12097
|
-
}
|
|
12098
|
-
if (Array.isArray(node.children)) {
|
|
12099
|
-
await Promise.all(node.children.map(inlineNodeAssets));
|
|
12100
|
-
}
|
|
12101
|
-
}
|
|
12102
|
-
async function inlineBrowserReachableImageAssets(config) {
|
|
12103
|
-
if (typeof window === "undefined" || typeof fetch === "undefined" || typeof FileReader === "undefined") {
|
|
12104
|
-
return config;
|
|
12105
|
-
}
|
|
12106
|
-
const cloned = JSON.parse(JSON.stringify(config));
|
|
12107
|
-
await Promise.all(
|
|
12108
|
-
(cloned.pages || []).flatMap((page) => (page.children || []).map(inlineNodeAssets))
|
|
12109
|
-
);
|
|
12110
|
-
return cloned;
|
|
12111
|
-
}
|
|
12112
11991
|
class PixldocsRenderer {
|
|
12113
11992
|
constructor(config) {
|
|
12114
11993
|
__publicField(this, "config");
|
|
@@ -12119,22 +11998,21 @@ class PixldocsRenderer {
|
|
|
12119
11998
|
* Mounts a hidden PreviewCanvas component and captures the Fabric canvas output.
|
|
12120
11999
|
*/
|
|
12121
12000
|
async render(templateConfig, options = {}) {
|
|
12122
|
-
const renderConfig = await inlineBrowserReachableImageAssets(templateConfig);
|
|
12123
12001
|
const pageIndex = options.pageIndex ?? 0;
|
|
12124
12002
|
const format = options.format ?? "png";
|
|
12125
12003
|
const quality = options.quality ?? 0.92;
|
|
12126
12004
|
const pixelRatio = options.pixelRatio ?? this.config.pixelRatio ?? 2;
|
|
12127
|
-
const canvasWidth =
|
|
12128
|
-
const canvasHeight =
|
|
12129
|
-
const page =
|
|
12005
|
+
const canvasWidth = templateConfig.canvas.width;
|
|
12006
|
+
const canvasHeight = templateConfig.canvas.height;
|
|
12007
|
+
const page = templateConfig.pages[pageIndex];
|
|
12130
12008
|
if (!page) {
|
|
12131
|
-
throw new Error(`Page index ${pageIndex} not found (template has ${
|
|
12009
|
+
throw new Error(`Page index ${pageIndex} not found (template has ${templateConfig.pages.length} pages)`);
|
|
12132
12010
|
}
|
|
12133
|
-
await ensureFontsForResolvedConfig(
|
|
12011
|
+
await ensureFontsForResolvedConfig(templateConfig);
|
|
12134
12012
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12135
12013
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12136
12014
|
const dataUrl = await this.renderPageViaPreviewCanvas(
|
|
12137
|
-
|
|
12015
|
+
templateConfig,
|
|
12138
12016
|
pageIndex,
|
|
12139
12017
|
pixelRatio,
|
|
12140
12018
|
format,
|
|
@@ -12186,17 +12064,16 @@ class PixldocsRenderer {
|
|
|
12186
12064
|
* This is the key building block for client-side vector PDF export.
|
|
12187
12065
|
*/
|
|
12188
12066
|
async renderPageSvg(templateConfig, pageIndex = 0) {
|
|
12189
|
-
const
|
|
12190
|
-
const page = renderConfig.pages[pageIndex];
|
|
12067
|
+
const page = templateConfig.pages[pageIndex];
|
|
12191
12068
|
if (!page) {
|
|
12192
|
-
throw new Error(`Page index ${pageIndex} not found (template has ${
|
|
12069
|
+
throw new Error(`Page index ${pageIndex} not found (template has ${templateConfig.pages.length} pages)`);
|
|
12193
12070
|
}
|
|
12194
|
-
await ensureFontsForResolvedConfig(
|
|
12071
|
+
await ensureFontsForResolvedConfig(templateConfig);
|
|
12195
12072
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12196
12073
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12197
|
-
const canvasWidth =
|
|
12198
|
-
const canvasHeight =
|
|
12199
|
-
return this.captureSvgViaPreviewCanvas(
|
|
12074
|
+
const canvasWidth = templateConfig.canvas.width;
|
|
12075
|
+
const canvasHeight = templateConfig.canvas.height;
|
|
12076
|
+
return this.captureSvgViaPreviewCanvas(templateConfig, pageIndex, canvasWidth, canvasHeight);
|
|
12200
12077
|
}
|
|
12201
12078
|
/**
|
|
12202
12079
|
* Render all pages and return SVG strings for each.
|
|
@@ -12529,7 +12406,7 @@ class PixldocsRenderer {
|
|
|
12529
12406
|
}
|
|
12530
12407
|
}
|
|
12531
12408
|
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality) {
|
|
12532
|
-
const {
|
|
12409
|
+
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
12533
12410
|
const canvasWidth = config.canvas.width;
|
|
12534
12411
|
const canvasHeight = config.canvas.height;
|
|
12535
12412
|
return new Promise((resolve, reject) => {
|
|
@@ -12537,14 +12414,13 @@ class PixldocsRenderer {
|
|
|
12537
12414
|
container.style.cssText = `
|
|
12538
12415
|
position: fixed; left: -99999px; top: -99999px;
|
|
12539
12416
|
width: ${canvasWidth}px; height: ${canvasHeight}px;
|
|
12540
|
-
overflow: hidden; pointer-events: none;
|
|
12417
|
+
overflow: hidden; pointer-events: none; opacity: 0;
|
|
12541
12418
|
`;
|
|
12542
12419
|
document.body.appendChild(container);
|
|
12543
12420
|
const timeout = setTimeout(() => {
|
|
12544
12421
|
cleanup();
|
|
12545
12422
|
reject(new Error("Render timeout (30s)"));
|
|
12546
12423
|
}, 3e4);
|
|
12547
|
-
let finished = false;
|
|
12548
12424
|
const cleanup = () => {
|
|
12549
12425
|
clearTimeout(timeout);
|
|
12550
12426
|
try {
|
|
@@ -12554,12 +12430,8 @@ class PixldocsRenderer {
|
|
|
12554
12430
|
container.remove();
|
|
12555
12431
|
};
|
|
12556
12432
|
const onReady = () => {
|
|
12557
|
-
if (finished) return;
|
|
12558
|
-
finished = true;
|
|
12559
12433
|
this.waitForCanvasScene(container, config, pageIndex).then(async () => {
|
|
12560
12434
|
try {
|
|
12561
|
-
await this.waitForStableTextMetrics(container, config, true);
|
|
12562
|
-
await this.waitForCanvasScene(container, config, pageIndex, 2500, 50);
|
|
12563
12435
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
12564
12436
|
const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
|
|
12565
12437
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
@@ -12571,8 +12443,8 @@ class PixldocsRenderer {
|
|
|
12571
12443
|
return;
|
|
12572
12444
|
}
|
|
12573
12445
|
const exportCanvas = document.createElement("canvas");
|
|
12574
|
-
exportCanvas.width =
|
|
12575
|
-
exportCanvas.height =
|
|
12446
|
+
exportCanvas.width = sourceCanvas.width;
|
|
12447
|
+
exportCanvas.height = sourceCanvas.height;
|
|
12576
12448
|
const exportCtx = exportCanvas.getContext("2d");
|
|
12577
12449
|
if (!exportCtx) {
|
|
12578
12450
|
cleanup();
|
|
@@ -12580,10 +12452,10 @@ class PixldocsRenderer {
|
|
|
12580
12452
|
return;
|
|
12581
12453
|
}
|
|
12582
12454
|
exportCtx.save();
|
|
12583
|
-
exportCtx.scale(
|
|
12455
|
+
exportCtx.scale(sourceCanvas.width / canvasWidth, sourceCanvas.height / canvasHeight);
|
|
12584
12456
|
this.paintPageBackground(exportCtx, config.pages[pageIndex], canvasWidth, canvasHeight);
|
|
12585
12457
|
exportCtx.restore();
|
|
12586
|
-
exportCtx.drawImage(sourceCanvas, 0, 0
|
|
12458
|
+
exportCtx.drawImage(sourceCanvas, 0, 0);
|
|
12587
12459
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
12588
12460
|
const dataUrl = exportCanvas.toDataURL(mimeType, quality);
|
|
12589
12461
|
cleanup();
|
|
@@ -12596,11 +12468,12 @@ class PixldocsRenderer {
|
|
|
12596
12468
|
};
|
|
12597
12469
|
const root = createRoot(container);
|
|
12598
12470
|
root.render(
|
|
12599
|
-
createElement(
|
|
12471
|
+
createElement(PreviewCanvas2, {
|
|
12600
12472
|
config,
|
|
12601
12473
|
pageIndex,
|
|
12602
|
-
zoom:
|
|
12474
|
+
zoom: pixelRatio,
|
|
12603
12475
|
absoluteZoom: true,
|
|
12476
|
+
skipFontReadyWait: true,
|
|
12604
12477
|
onReady
|
|
12605
12478
|
})
|
|
12606
12479
|
);
|
|
@@ -12617,19 +12490,18 @@ class PixldocsRenderer {
|
|
|
12617
12490
|
// document space (e.g. 612x792) instead of inflated pixel space.
|
|
12618
12491
|
captureSvgViaPreviewCanvas(config, pageIndex, canvasWidth, canvasHeight) {
|
|
12619
12492
|
return new Promise(async (resolve, reject) => {
|
|
12620
|
-
const {
|
|
12493
|
+
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
12621
12494
|
const container = document.createElement("div");
|
|
12622
12495
|
container.style.cssText = `
|
|
12623
12496
|
position: fixed; left: -99999px; top: -99999px;
|
|
12624
12497
|
width: ${canvasWidth}px; height: ${canvasHeight}px;
|
|
12625
|
-
overflow: hidden; pointer-events: none;
|
|
12498
|
+
overflow: hidden; pointer-events: none; opacity: 0;
|
|
12626
12499
|
`;
|
|
12627
12500
|
document.body.appendChild(container);
|
|
12628
12501
|
const timeout = setTimeout(() => {
|
|
12629
12502
|
cleanup();
|
|
12630
12503
|
reject(new Error("SVG render timeout (30s)"));
|
|
12631
12504
|
}, 3e4);
|
|
12632
|
-
let finished = false;
|
|
12633
12505
|
const cleanup = () => {
|
|
12634
12506
|
clearTimeout(timeout);
|
|
12635
12507
|
try {
|
|
@@ -12639,13 +12511,9 @@ class PixldocsRenderer {
|
|
|
12639
12511
|
container.remove();
|
|
12640
12512
|
};
|
|
12641
12513
|
const onReady = () => {
|
|
12642
|
-
if (finished) return;
|
|
12643
|
-
finished = true;
|
|
12644
12514
|
this.waitForCanvasScene(container, config, pageIndex).then(async () => {
|
|
12645
12515
|
var _a, _b;
|
|
12646
12516
|
try {
|
|
12647
|
-
await this.waitForStableTextMetrics(container, config, true);
|
|
12648
|
-
await this.waitForCanvasScene(container, config, pageIndex, 2500, 50);
|
|
12649
12517
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
12650
12518
|
if (!fabricInstance) {
|
|
12651
12519
|
cleanup();
|
|
@@ -12698,12 +12566,13 @@ class PixldocsRenderer {
|
|
|
12698
12566
|
};
|
|
12699
12567
|
const root = createRoot(container);
|
|
12700
12568
|
root.render(
|
|
12701
|
-
createElement(
|
|
12569
|
+
createElement(PreviewCanvas2, {
|
|
12702
12570
|
config,
|
|
12703
12571
|
pageIndex,
|
|
12704
12572
|
zoom: 1,
|
|
12705
12573
|
// 1:1 — no UI scaling for SVG capture
|
|
12706
12574
|
absoluteZoom: true,
|
|
12575
|
+
skipFontReadyWait: true,
|
|
12707
12576
|
onReady
|
|
12708
12577
|
})
|
|
12709
12578
|
);
|
|
@@ -12764,12 +12633,21 @@ class PixldocsRenderer {
|
|
|
12764
12633
|
* using the global __fabricCanvasRegistry (set by PageCanvas).
|
|
12765
12634
|
*/
|
|
12766
12635
|
getFabricCanvasFromContainer(container) {
|
|
12767
|
-
|
|
12636
|
+
const registry2 = window.__fabricCanvasRegistry;
|
|
12637
|
+
if (registry2 instanceof Map) {
|
|
12638
|
+
for (const entry of registry2.values()) {
|
|
12639
|
+
const canvas = (entry == null ? void 0 : entry.canvas) || entry;
|
|
12640
|
+
if (!canvas || typeof canvas.toSVG !== "function") continue;
|
|
12641
|
+
const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
|
|
12642
|
+
if (el && container.contains(el)) return canvas;
|
|
12643
|
+
}
|
|
12644
|
+
}
|
|
12645
|
+
return null;
|
|
12768
12646
|
}
|
|
12769
|
-
async waitForStableTextMetrics(container, config
|
|
12647
|
+
async waitForStableTextMetrics(container, config) {
|
|
12770
12648
|
if (typeof document !== "undefined") {
|
|
12771
|
-
|
|
12772
|
-
await this.waitForRelevantFonts(config
|
|
12649
|
+
void ensureFontsForResolvedConfig(config);
|
|
12650
|
+
await this.waitForRelevantFonts(config);
|
|
12773
12651
|
}
|
|
12774
12652
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
12775
12653
|
if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
|
|
@@ -12778,7 +12656,59 @@ class PixldocsRenderer {
|
|
|
12778
12656
|
clearFabricCharCache();
|
|
12779
12657
|
clearMeasurementCache();
|
|
12780
12658
|
};
|
|
12781
|
-
const reflowTextboxes = () =>
|
|
12659
|
+
const reflowTextboxes = () => {
|
|
12660
|
+
var _a, _b, _c;
|
|
12661
|
+
const walk = (obj) => {
|
|
12662
|
+
var _a2, _b2, _c2, _d;
|
|
12663
|
+
if (!obj) return;
|
|
12664
|
+
const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
|
|
12665
|
+
if (children.length) children.forEach(walk);
|
|
12666
|
+
const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text" || obj.isEditing !== void 0);
|
|
12667
|
+
if (isTextObject) {
|
|
12668
|
+
const saved = {
|
|
12669
|
+
width: obj.width,
|
|
12670
|
+
scaleX: obj.scaleX,
|
|
12671
|
+
scaleY: obj.scaleY
|
|
12672
|
+
};
|
|
12673
|
+
const resetTextboxLayoutInternals = () => {
|
|
12674
|
+
var _a3;
|
|
12675
|
+
(_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
|
|
12676
|
+
obj.__charBounds = [];
|
|
12677
|
+
obj.__lineWidths = [];
|
|
12678
|
+
obj.__lineHeights = [];
|
|
12679
|
+
obj.__graphemeLines = [];
|
|
12680
|
+
obj._textLines = [];
|
|
12681
|
+
obj.textLines = [];
|
|
12682
|
+
obj._styleMap = null;
|
|
12683
|
+
obj.styleMap = null;
|
|
12684
|
+
obj.dirty = true;
|
|
12685
|
+
};
|
|
12686
|
+
resetTextboxLayoutInternals();
|
|
12687
|
+
obj.initDimensions();
|
|
12688
|
+
if (saved.width != null) {
|
|
12689
|
+
(_a2 = obj.set) == null ? void 0 : _a2.call(obj, {
|
|
12690
|
+
width: saved.width,
|
|
12691
|
+
scaleX: saved.scaleX,
|
|
12692
|
+
scaleY: saved.scaleY
|
|
12693
|
+
});
|
|
12694
|
+
resetTextboxLayoutInternals();
|
|
12695
|
+
obj.initDimensions();
|
|
12696
|
+
}
|
|
12697
|
+
(_b2 = obj.set) == null ? void 0 : _b2.call(obj, {
|
|
12698
|
+
width: saved.width,
|
|
12699
|
+
scaleX: saved.scaleX,
|
|
12700
|
+
scaleY: saved.scaleY,
|
|
12701
|
+
dirty: true
|
|
12702
|
+
});
|
|
12703
|
+
(_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
|
|
12704
|
+
(_d = obj.setCoords) == null ? void 0 : _d.call(obj);
|
|
12705
|
+
}
|
|
12706
|
+
};
|
|
12707
|
+
fabricInstance.getObjects().forEach(walk);
|
|
12708
|
+
(_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
|
|
12709
|
+
(_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
|
|
12710
|
+
(_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
|
|
12711
|
+
};
|
|
12782
12712
|
clearTextMetricCaches();
|
|
12783
12713
|
await waitForPaint();
|
|
12784
12714
|
reflowTextboxes();
|
|
@@ -13512,6 +13442,9 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
13512
13442
|
const resolved = resolveFontWeight(weight);
|
|
13513
13443
|
const isItalic = /italic|oblique/i.test(styleRaw);
|
|
13514
13444
|
const jsPdfName = getEmbeddedJsPDFFontName(clean, resolved, isItalic);
|
|
13445
|
+
el.setAttribute("data-source-font-family", clean);
|
|
13446
|
+
el.setAttribute("data-source-font-weight", String(resolved));
|
|
13447
|
+
el.setAttribute("data-source-font-style", isItalic ? "italic" : "normal");
|
|
13515
13448
|
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
13516
13449
|
const hasDevanagari = containsDevanagari(directText);
|
|
13517
13450
|
if (hasDevanagari && directText.length > 0) {
|
|
@@ -14493,24 +14426,89 @@ async function svg2pdfWithDomMount(svg, pdf, opts) {
|
|
|
14493
14426
|
if (wrap.parentNode) document.body.removeChild(wrap);
|
|
14494
14427
|
}
|
|
14495
14428
|
}
|
|
14496
|
-
function convertTextDecorationsToLines(svg) {
|
|
14429
|
+
async function convertTextDecorationsToLines(svg) {
|
|
14497
14430
|
const doc = svg.ownerDocument;
|
|
14498
14431
|
if (!doc) return;
|
|
14499
|
-
|
|
14432
|
+
const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
|
|
14433
|
+
var _a, _b;
|
|
14434
|
+
let current = el;
|
|
14435
|
+
while (current) {
|
|
14436
|
+
const attrValue = (_a = current.getAttribute(attr)) == null ? void 0 : _a.trim();
|
|
14437
|
+
if (attrValue) return attrValue;
|
|
14438
|
+
const styleValue = (_b = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b.trim();
|
|
14439
|
+
if (styleValue) return styleValue;
|
|
14440
|
+
current = current.parentElement;
|
|
14441
|
+
}
|
|
14442
|
+
return null;
|
|
14443
|
+
};
|
|
14444
|
+
if (typeof document !== "undefined" && document.fonts) {
|
|
14445
|
+
const fontFamilies = /* @__PURE__ */ new Set();
|
|
14446
|
+
for (const textEl of svg.querySelectorAll("text")) {
|
|
14447
|
+
const ff = textEl.getAttribute("data-source-font-family") || textEl.getAttribute("font-family");
|
|
14448
|
+
if (ff) fontFamilies.add(ff.replace(/'/g, ""));
|
|
14449
|
+
for (const tspan of textEl.querySelectorAll("tspan")) {
|
|
14450
|
+
const tff = tspan.getAttribute("data-source-font-family") || tspan.getAttribute("font-family");
|
|
14451
|
+
if (tff) fontFamilies.add(tff.replace(/'/g, ""));
|
|
14452
|
+
}
|
|
14453
|
+
}
|
|
14454
|
+
await Promise.all(Array.from(fontFamilies).flatMap((ff) => [
|
|
14455
|
+
document.fonts.load(`16px "${ff}"`).then(() => {
|
|
14456
|
+
}).catch(() => {
|
|
14457
|
+
}),
|
|
14458
|
+
document.fonts.load(`bold 16px "${ff}"`).then(() => {
|
|
14459
|
+
}).catch(() => {
|
|
14460
|
+
})
|
|
14461
|
+
]));
|
|
14462
|
+
await document.fonts.ready;
|
|
14463
|
+
}
|
|
14464
|
+
let tempContainer = null;
|
|
14465
|
+
let liveSvg = null;
|
|
14466
|
+
try {
|
|
14467
|
+
if (typeof document !== "undefined") {
|
|
14468
|
+
tempContainer = document.createElement("div");
|
|
14469
|
+
tempContainer.style.cssText = "position:fixed;left:-9999px;top:-9999px;visibility:hidden;pointer-events:none;";
|
|
14470
|
+
document.body.appendChild(tempContainer);
|
|
14471
|
+
const clone = svg.cloneNode(true);
|
|
14472
|
+
for (const textNode of clone.querySelectorAll("text, tspan, textPath")) {
|
|
14473
|
+
const sourceFamily = textNode.getAttribute("data-source-font-family");
|
|
14474
|
+
const sourceWeight = textNode.getAttribute("data-source-font-weight");
|
|
14475
|
+
const sourceStyle = textNode.getAttribute("data-source-font-style");
|
|
14476
|
+
if (sourceFamily) textNode.setAttribute("font-family", sourceFamily);
|
|
14477
|
+
if (sourceWeight) textNode.setAttribute("font-weight", sourceWeight);
|
|
14478
|
+
if (sourceStyle) textNode.setAttribute("font-style", sourceStyle);
|
|
14479
|
+
const inlineStyle = textNode.getAttribute("style") || "";
|
|
14480
|
+
const stylePairs = inlineStyle.split(";").map((part) => part.trim()).filter(Boolean).filter((part) => !/^font-family\s*:/i.test(part) && !/^font-weight\s*:/i.test(part) && !/^font-style\s*:/i.test(part));
|
|
14481
|
+
if (sourceFamily) stylePairs.push(`font-family: ${sourceFamily}`);
|
|
14482
|
+
if (sourceWeight) stylePairs.push(`font-weight: ${sourceWeight}`);
|
|
14483
|
+
if (sourceStyle) stylePairs.push(`font-style: ${sourceStyle}`);
|
|
14484
|
+
if (stylePairs.length > 0) textNode.setAttribute("style", stylePairs.join("; "));
|
|
14485
|
+
}
|
|
14486
|
+
tempContainer.appendChild(clone);
|
|
14487
|
+
liveSvg = clone;
|
|
14488
|
+
}
|
|
14489
|
+
} catch {
|
|
14490
|
+
}
|
|
14500
14491
|
let ctx = null;
|
|
14501
14492
|
try {
|
|
14502
|
-
|
|
14493
|
+
const realDoc = typeof document !== "undefined" ? document : doc;
|
|
14494
|
+
const measureCanvas = realDoc.createElement("canvas");
|
|
14503
14495
|
ctx = measureCanvas.getContext("2d");
|
|
14504
14496
|
} catch {
|
|
14505
14497
|
}
|
|
14506
14498
|
const textEls = Array.from(svg.querySelectorAll("text"));
|
|
14507
|
-
|
|
14499
|
+
const liveTextEls = liveSvg ? Array.from(liveSvg.querySelectorAll("text")) : null;
|
|
14500
|
+
for (let ti = 0; ti < textEls.length; ti++) {
|
|
14501
|
+
const textEl = textEls[ti];
|
|
14502
|
+
const liveTextEl = liveTextEls == null ? void 0 : liveTextEls[ti];
|
|
14508
14503
|
const tspans = Array.from(textEl.querySelectorAll("tspan"));
|
|
14509
14504
|
if (tspans.length === 0) continue;
|
|
14505
|
+
const liveTspans = liveTextEl ? Array.from(liveTextEl.querySelectorAll("tspan")) : null;
|
|
14510
14506
|
const textDecOnText = (textEl.getAttribute("text-decoration") || "").toLowerCase();
|
|
14511
14507
|
const textStyleDec = (getInlineStyleValue(textEl, "text-decoration") || "").toLowerCase();
|
|
14512
14508
|
const textHasUnderline = textDecOnText.includes("underline") || textStyleDec.includes("underline");
|
|
14513
|
-
for (
|
|
14509
|
+
for (let si = 0; si < tspans.length; si++) {
|
|
14510
|
+
const tspan = tspans[si];
|
|
14511
|
+
const liveTspan = liveTspans == null ? void 0 : liveTspans[si];
|
|
14514
14512
|
const tspanDec = (tspan.getAttribute("text-decoration") || "").toLowerCase();
|
|
14515
14513
|
const tspanStyleDec = (getInlineStyleValue(tspan, "text-decoration") || "").toLowerCase();
|
|
14516
14514
|
const hasUnderline = tspanDec.includes("underline") || tspanStyleDec.includes("underline") || textHasUnderline;
|
|
@@ -14524,41 +14522,73 @@ function convertTextDecorationsToLines(svg) {
|
|
|
14524
14522
|
const fontSize = parseFloat(
|
|
14525
14523
|
tspan.getAttribute("font-size") || textEl.getAttribute("font-size") || "16"
|
|
14526
14524
|
);
|
|
14527
|
-
const fontFamily = tspan.getAttribute("font-family") || textEl.getAttribute("font-family") || "sans-serif";
|
|
14528
|
-
const fontWeight = tspan.getAttribute("font-weight") || textEl.getAttribute("font-weight") || "normal";
|
|
14525
|
+
const fontFamily = tspan.getAttribute("data-source-font-family") || textEl.getAttribute("data-source-font-family") || resolveInheritedSvgValue(tspan, "font-family") || "sans-serif";
|
|
14526
|
+
const fontWeight = tspan.getAttribute("data-source-font-weight") || textEl.getAttribute("data-source-font-weight") || resolveInheritedSvgValue(tspan, "font-weight") || "normal";
|
|
14527
|
+
const fontStyle = tspan.getAttribute("data-source-font-style") || textEl.getAttribute("data-source-font-style") || resolveInheritedSvgValue(tspan, "font-style") || "normal";
|
|
14529
14528
|
const fill = tspan.getAttribute("fill") || textEl.getAttribute("fill") || "#000000";
|
|
14530
|
-
let textWidth = 0;
|
|
14531
|
-
if (
|
|
14529
|
+
let textWidth = content.length * fontSize * 0.6;
|
|
14530
|
+
if (ctx) {
|
|
14531
|
+
ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily.replace(/'/g, "")}`;
|
|
14532
|
+
textWidth = ctx.measureText(content).width;
|
|
14533
|
+
}
|
|
14534
|
+
const textAnchorRaw = (resolveInheritedSvgValue(tspan, "text-anchor") || "start").toLowerCase();
|
|
14535
|
+
const textAnchor = textAnchorRaw === "middle" || textAnchorRaw === "end" ? textAnchorRaw : "start";
|
|
14536
|
+
let lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
14537
|
+
let lineEndX = lineStartX + textWidth;
|
|
14538
|
+
let baselineY = y;
|
|
14539
|
+
if (liveTspan) {
|
|
14532
14540
|
try {
|
|
14533
|
-
|
|
14541
|
+
const liveCharCount = liveTspan.getNumberOfChars();
|
|
14542
|
+
const svgWidth = liveTspan.getComputedTextLength();
|
|
14543
|
+
if (Number.isFinite(svgWidth) && svgWidth > 0) {
|
|
14544
|
+
textWidth = Math.max(textWidth, svgWidth);
|
|
14545
|
+
}
|
|
14546
|
+
if (liveCharCount > 0) {
|
|
14547
|
+
const start = liveTspan.getStartPositionOfChar(0);
|
|
14548
|
+
const end = liveTspan.getEndPositionOfChar(liveCharCount - 1);
|
|
14549
|
+
if (Number.isFinite(start.x)) lineStartX = start.x;
|
|
14550
|
+
if (Number.isFinite(start.y)) baselineY = start.y;
|
|
14551
|
+
if (Number.isFinite(end.x)) lineEndX = Math.max(end.x, lineStartX + textWidth);
|
|
14552
|
+
if (Number.isFinite(end.y) && !Number.isFinite(baselineY)) baselineY = end.y;
|
|
14553
|
+
} else {
|
|
14554
|
+
lineEndX = lineStartX + textWidth;
|
|
14555
|
+
}
|
|
14556
|
+
const bbox = liveTspan.getBBox();
|
|
14557
|
+
if (Number.isFinite(bbox.x) && Number.isFinite(bbox.width)) {
|
|
14558
|
+
lineStartX = Math.min(lineStartX, bbox.x);
|
|
14559
|
+
lineEndX = Math.max(lineEndX, bbox.x + bbox.width);
|
|
14560
|
+
}
|
|
14534
14561
|
} catch {
|
|
14535
14562
|
}
|
|
14536
14563
|
}
|
|
14537
|
-
if (!
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
textWidth = ctx.measureText(content).width;
|
|
14541
|
-
} else {
|
|
14542
|
-
textWidth = content.length * fontSize * 0.6;
|
|
14543
|
-
}
|
|
14564
|
+
if (!(lineEndX > lineStartX)) {
|
|
14565
|
+
lineStartX = x - (textAnchor === "middle" ? textWidth / 2 : textAnchor === "end" ? textWidth : 0);
|
|
14566
|
+
lineEndX = lineStartX + textWidth;
|
|
14544
14567
|
}
|
|
14545
|
-
const underlineY =
|
|
14568
|
+
const underlineY = baselineY + fontSize * 0.15;
|
|
14546
14569
|
const thickness = Math.max(0.5, fontSize * 0.066667);
|
|
14547
14570
|
const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
14548
|
-
line.setAttribute("x1", String(
|
|
14571
|
+
line.setAttribute("x1", String(lineStartX));
|
|
14549
14572
|
line.setAttribute("y1", String(underlineY));
|
|
14550
|
-
line.setAttribute("x2", String(
|
|
14573
|
+
line.setAttribute("x2", String(lineEndX));
|
|
14551
14574
|
line.setAttribute("y2", String(underlineY));
|
|
14552
14575
|
line.setAttribute("stroke", fill.startsWith("url(") ? "#000000" : fill);
|
|
14553
14576
|
line.setAttribute("stroke-width", String(thickness));
|
|
14577
|
+
line.setAttribute("stroke-linecap", "butt");
|
|
14554
14578
|
line.setAttribute("fill", "none");
|
|
14555
14579
|
if (textEl.parentElement) {
|
|
14556
14580
|
textEl.parentElement.insertBefore(line, textEl.nextSibling);
|
|
14557
14581
|
}
|
|
14558
14582
|
}
|
|
14559
14583
|
}
|
|
14584
|
+
if (tempContainer) {
|
|
14585
|
+
try {
|
|
14586
|
+
document.body.removeChild(tempContainer);
|
|
14587
|
+
} catch {
|
|
14588
|
+
}
|
|
14589
|
+
}
|
|
14560
14590
|
}
|
|
14561
|
-
function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
|
|
14591
|
+
async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
|
|
14562
14592
|
try {
|
|
14563
14593
|
const parser = new DOMParser();
|
|
14564
14594
|
const processedSvg = inlineNestedSvgImageDataUris(rawSvg, parser);
|
|
@@ -14587,7 +14617,6 @@ function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, opti
|
|
|
14587
14617
|
stripSuspiciousFullPageOverlayNodes(svgToDraw);
|
|
14588
14618
|
if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
|
|
14589
14619
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
14590
|
-
convertTextDecorationsToLines(svgToDraw);
|
|
14591
14620
|
return svgToDraw;
|
|
14592
14621
|
} catch {
|
|
14593
14622
|
return null;
|
|
@@ -14690,7 +14719,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
14690
14719
|
const hasGradient = !!((_b = (_a = page.backgroundGradient) == null ? void 0 : _a.stops) == null ? void 0 : _b.length);
|
|
14691
14720
|
drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
|
|
14692
14721
|
const shouldStripBg = stripPageBackground ?? hasGradient;
|
|
14693
|
-
let processedSvg = prepareLiveCanvasSvgForPdf(page.svg, page.width, page.height, `page-${i + 1}`, {
|
|
14722
|
+
let processedSvg = await prepareLiveCanvasSvgForPdf(page.svg, page.width, page.height, `page-${i + 1}`, {
|
|
14694
14723
|
stripPageBackground: shouldStripBg
|
|
14695
14724
|
});
|
|
14696
14725
|
if (processedSvg) {
|
|
@@ -14699,6 +14728,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
14699
14728
|
const reParser = new DOMParser();
|
|
14700
14729
|
const reDoc = reParser.parseFromString(rewrittenSvg, "image/svg+xml");
|
|
14701
14730
|
processedSvg = reDoc.documentElement;
|
|
14731
|
+
await convertTextDecorationsToLines(processedSvg);
|
|
14702
14732
|
}
|
|
14703
14733
|
if (processedSvg) {
|
|
14704
14734
|
pdf.setFillColor(0, 0, 0);
|