@pixldocs/canvas-renderer 0.3.15 → 0.3.17
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/canvasWatermark-BgiyK4gu.js +115 -0
- package/dist/canvasWatermark-BgiyK4gu.js.map +1 -0
- package/dist/canvasWatermark-C7Jk6O8G.cjs +115 -0
- package/dist/canvasWatermark-C7Jk6O8G.cjs.map +1 -0
- package/dist/index.cjs +83 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +83 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const WATERMARK_ID_PREFIX = "__wm_";
|
|
2
|
+
function getWatermarkText() {
|
|
3
|
+
try {
|
|
4
|
+
const host = window.location.hostname;
|
|
5
|
+
if (host.includes("biomaker")) return "biomaker.app";
|
|
6
|
+
} catch {
|
|
7
|
+
}
|
|
8
|
+
return "pixldocs.com";
|
|
9
|
+
}
|
|
10
|
+
function getLuminance(color) {
|
|
11
|
+
if (!color || color === "transparent" || color === "none") return 1;
|
|
12
|
+
let r = 255, g = 255, b = 255;
|
|
13
|
+
if (color.startsWith("#")) {
|
|
14
|
+
const hex = color.slice(1);
|
|
15
|
+
const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
|
|
16
|
+
const n = parseInt(full, 16);
|
|
17
|
+
r = n >> 16 & 255;
|
|
18
|
+
g = n >> 8 & 255;
|
|
19
|
+
b = n & 255;
|
|
20
|
+
} else {
|
|
21
|
+
const m = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
22
|
+
if (m) {
|
|
23
|
+
r = +m[1];
|
|
24
|
+
g = +m[2];
|
|
25
|
+
b = +m[3];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
29
|
+
}
|
|
30
|
+
function getWatermarkColor(bgColor) {
|
|
31
|
+
const lum = getLuminance(bgColor);
|
|
32
|
+
return lum < 0.45 ? "#cccccc" : "#808080";
|
|
33
|
+
}
|
|
34
|
+
function getPageBgColor(page, canvasBg) {
|
|
35
|
+
var _a;
|
|
36
|
+
const children = page.children || [];
|
|
37
|
+
for (const child of children) {
|
|
38
|
+
if (child.type === "shape" && child.fill && child.fill !== "transparent" && child.fill !== "none") {
|
|
39
|
+
return child.fill;
|
|
40
|
+
}
|
|
41
|
+
if ((_a = child.children) == null ? void 0 : _a.length) {
|
|
42
|
+
for (const gc of child.children) {
|
|
43
|
+
if (gc.type === "shape" && gc.fill && gc.fill !== "transparent" && gc.fill !== "none") {
|
|
44
|
+
return gc.fill;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return canvasBg;
|
|
50
|
+
}
|
|
51
|
+
function generateWatermarkElements(canvasWidth, canvasHeight, wmColor) {
|
|
52
|
+
const elements = [];
|
|
53
|
+
const text = getWatermarkText();
|
|
54
|
+
const fontSize = 18;
|
|
55
|
+
const angle = -30;
|
|
56
|
+
const estimatedTextWidth = text.length * fontSize * 0.55;
|
|
57
|
+
const spacingX = Math.max(160, estimatedTextWidth + 30);
|
|
58
|
+
const spacingY = 90;
|
|
59
|
+
const startX = -canvasWidth * 0.3;
|
|
60
|
+
const startY = -canvasHeight * 0.3;
|
|
61
|
+
const endX = canvasWidth * 1.5;
|
|
62
|
+
const endY = canvasHeight * 1.5;
|
|
63
|
+
let idx = 0;
|
|
64
|
+
for (let y = startY; y < endY; y += spacingY) {
|
|
65
|
+
for (let x = startX; x < endX; x += spacingX) {
|
|
66
|
+
elements.push({
|
|
67
|
+
id: `${WATERMARK_ID_PREFIX}${idx++}`,
|
|
68
|
+
type: "text",
|
|
69
|
+
left: x,
|
|
70
|
+
top: y,
|
|
71
|
+
width: 200,
|
|
72
|
+
height: 40,
|
|
73
|
+
text,
|
|
74
|
+
fontFamily: "Montserrat",
|
|
75
|
+
fontSize,
|
|
76
|
+
fontWeight: "600",
|
|
77
|
+
fill: wmColor,
|
|
78
|
+
angle,
|
|
79
|
+
selectable: false,
|
|
80
|
+
locked: true,
|
|
81
|
+
visible: true,
|
|
82
|
+
opacity: 0.14,
|
|
83
|
+
scaleX: 1,
|
|
84
|
+
scaleY: 1,
|
|
85
|
+
letterSpacing: 4
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return elements;
|
|
90
|
+
}
|
|
91
|
+
function injectWatermark(config) {
|
|
92
|
+
var _a, _b, _c;
|
|
93
|
+
const canvasWidth = ((_a = config.canvas) == null ? void 0 : _a.width) || 612;
|
|
94
|
+
const canvasHeight = ((_b = config.canvas) == null ? void 0 : _b.height) || 792;
|
|
95
|
+
const canvasBg = (_c = config.canvas) == null ? void 0 : _c.backgroundColor;
|
|
96
|
+
return {
|
|
97
|
+
...config,
|
|
98
|
+
pages: config.pages.map((page) => {
|
|
99
|
+
const pageBg = getPageBgColor(page, canvasBg);
|
|
100
|
+
const wmColor = getWatermarkColor(pageBg);
|
|
101
|
+
const watermarkElements = generateWatermarkElements(canvasWidth, canvasHeight, wmColor);
|
|
102
|
+
return {
|
|
103
|
+
...page,
|
|
104
|
+
children: [
|
|
105
|
+
...page.children || [],
|
|
106
|
+
...watermarkElements
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
})
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
injectWatermark
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=canvasWatermark-BgiyK4gu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasWatermark-BgiyK4gu.js","sources":["../../../src/lib/canvasWatermark.ts"],"sourcesContent":["/**\n * Injects diagonal \"PixlDocs\" watermark text elements into a TemplateConfig's pages.\n * The watermark elements are rendered as actual canvas text objects,\n * making them impossible to remove via browser inspect.\n */\n\nimport type { TemplateConfig, CanvasElement } from '@/types/editor';\n\nconst WATERMARK_ID_PREFIX = '__wm_';\n\n/** Determine watermark text based on current domain */\nfunction getWatermarkText(): string {\n try {\n const host = window.location.hostname;\n if (host.includes('biomaker')) return 'biomaker.app';\n } catch {}\n return 'pixldocs.com';\n}\n\n/** Parse a hex/rgb color to luminance (0=black, 1=white) */\nfunction getLuminance(color: string | undefined): number {\n if (!color || color === 'transparent' || color === 'none') return 1; // assume light\n let r = 255, g = 255, b = 255;\n if (color.startsWith('#')) {\n const hex = color.slice(1);\n const full = hex.length === 3 ? hex.split('').map(c => c + c).join('') : hex;\n const n = parseInt(full, 16);\n r = (n >> 16) & 255;\n g = (n >> 8) & 255;\n b = n & 255;\n } else {\n const m = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (m) { r = +m[1]; g = +m[2]; b = +m[3]; }\n }\n return (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n}\n\n/** Pick watermark color that contrasts with page background */\nfunction getWatermarkColor(bgColor: string | undefined): string {\n const lum = getLuminance(bgColor);\n // Dark bg → light watermark, Light bg → dark watermark\n return lum < 0.45 ? '#cccccc' : '#808080';\n}\n\n/** Find the dominant background color of a page */\nfunction getPageBgColor(page: { children?: any[] }, canvasBg?: string): string | undefined {\n const children = page.children || [];\n for (const child of children) {\n if (child.type === 'shape' && child.fill && child.fill !== 'transparent' && child.fill !== 'none') {\n return child.fill;\n }\n // Check inside groups (background groups)\n if (child.children?.length) {\n for (const gc of child.children) {\n if (gc.type === 'shape' && gc.fill && gc.fill !== 'transparent' && gc.fill !== 'none') {\n return gc.fill;\n }\n }\n }\n }\n return canvasBg;\n}\n\n/** Generate watermark text elements for a given canvas size */\nfunction generateWatermarkElements(\n canvasWidth: number,\n canvasHeight: number,\n wmColor: string,\n): CanvasElement[] {\n const elements: CanvasElement[] = [];\n const text = getWatermarkText();\n const fontSize = 18;\n const angle = -30;\n // Adjust spacing based on text length to prevent overlap\n const estimatedTextWidth = text.length * fontSize * 0.55;\n const spacingX = Math.max(160, estimatedTextWidth + 30);\n const spacingY = 90;\n\n const startX = -canvasWidth * 0.3;\n const startY = -canvasHeight * 0.3;\n const endX = canvasWidth * 1.5;\n const endY = canvasHeight * 1.5;\n\n let idx = 0;\n for (let y = startY; y < endY; y += spacingY) {\n for (let x = startX; x < endX; x += spacingX) {\n elements.push({\n id: `${WATERMARK_ID_PREFIX}${idx++}`,\n type: 'text',\n left: x,\n top: y,\n width: 200,\n height: 40,\n text,\n fontFamily: 'Montserrat',\n fontSize,\n fontWeight: '600',\n fill: wmColor,\n angle,\n selectable: false,\n locked: true,\n visible: true,\n opacity: 0.14,\n scaleX: 1,\n scaleY: 1,\n letterSpacing: 4,\n } as CanvasElement);\n }\n }\n return elements;\n}\n\n/**\n * Returns a new config with watermark elements injected into every page.\n * The original config is NOT mutated.\n */\nexport function injectWatermark(config: TemplateConfig): TemplateConfig {\n const canvasWidth = config.canvas?.width || 612;\n const canvasHeight = config.canvas?.height || 792;\n const canvasBg = (config.canvas as any)?.backgroundColor;\n\n return {\n ...config,\n pages: config.pages.map((page) => {\n const pageBg = getPageBgColor(page as any, canvasBg);\n const wmColor = getWatermarkColor(pageBg);\n const watermarkElements = generateWatermarkElements(canvasWidth, canvasHeight, wmColor);\n return {\n ...page,\n children: [\n ...(page.children || []),\n ...watermarkElements,\n ],\n };\n }),\n };\n}\n\n/** Check if an element ID is a watermark element */\nexport function isWatermarkElement(id: string): boolean {\n return id.startsWith(WATERMARK_ID_PREFIX);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const WATERMARK_ID_PREFIX = "__wm_";
|
|
4
|
+
function getWatermarkText() {
|
|
5
|
+
try {
|
|
6
|
+
const host = window.location.hostname;
|
|
7
|
+
if (host.includes("biomaker")) return "biomaker.app";
|
|
8
|
+
} catch {
|
|
9
|
+
}
|
|
10
|
+
return "pixldocs.com";
|
|
11
|
+
}
|
|
12
|
+
function getLuminance(color) {
|
|
13
|
+
if (!color || color === "transparent" || color === "none") return 1;
|
|
14
|
+
let r = 255, g = 255, b = 255;
|
|
15
|
+
if (color.startsWith("#")) {
|
|
16
|
+
const hex = color.slice(1);
|
|
17
|
+
const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
|
|
18
|
+
const n = parseInt(full, 16);
|
|
19
|
+
r = n >> 16 & 255;
|
|
20
|
+
g = n >> 8 & 255;
|
|
21
|
+
b = n & 255;
|
|
22
|
+
} else {
|
|
23
|
+
const m = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
24
|
+
if (m) {
|
|
25
|
+
r = +m[1];
|
|
26
|
+
g = +m[2];
|
|
27
|
+
b = +m[3];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
31
|
+
}
|
|
32
|
+
function getWatermarkColor(bgColor) {
|
|
33
|
+
const lum = getLuminance(bgColor);
|
|
34
|
+
return lum < 0.45 ? "#cccccc" : "#808080";
|
|
35
|
+
}
|
|
36
|
+
function getPageBgColor(page, canvasBg) {
|
|
37
|
+
var _a;
|
|
38
|
+
const children = page.children || [];
|
|
39
|
+
for (const child of children) {
|
|
40
|
+
if (child.type === "shape" && child.fill && child.fill !== "transparent" && child.fill !== "none") {
|
|
41
|
+
return child.fill;
|
|
42
|
+
}
|
|
43
|
+
if ((_a = child.children) == null ? void 0 : _a.length) {
|
|
44
|
+
for (const gc of child.children) {
|
|
45
|
+
if (gc.type === "shape" && gc.fill && gc.fill !== "transparent" && gc.fill !== "none") {
|
|
46
|
+
return gc.fill;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return canvasBg;
|
|
52
|
+
}
|
|
53
|
+
function generateWatermarkElements(canvasWidth, canvasHeight, wmColor) {
|
|
54
|
+
const elements = [];
|
|
55
|
+
const text = getWatermarkText();
|
|
56
|
+
const fontSize = 18;
|
|
57
|
+
const angle = -30;
|
|
58
|
+
const estimatedTextWidth = text.length * fontSize * 0.55;
|
|
59
|
+
const spacingX = Math.max(160, estimatedTextWidth + 30);
|
|
60
|
+
const spacingY = 90;
|
|
61
|
+
const startX = -canvasWidth * 0.3;
|
|
62
|
+
const startY = -canvasHeight * 0.3;
|
|
63
|
+
const endX = canvasWidth * 1.5;
|
|
64
|
+
const endY = canvasHeight * 1.5;
|
|
65
|
+
let idx = 0;
|
|
66
|
+
for (let y = startY; y < endY; y += spacingY) {
|
|
67
|
+
for (let x = startX; x < endX; x += spacingX) {
|
|
68
|
+
elements.push({
|
|
69
|
+
id: `${WATERMARK_ID_PREFIX}${idx++}`,
|
|
70
|
+
type: "text",
|
|
71
|
+
left: x,
|
|
72
|
+
top: y,
|
|
73
|
+
width: 200,
|
|
74
|
+
height: 40,
|
|
75
|
+
text,
|
|
76
|
+
fontFamily: "Montserrat",
|
|
77
|
+
fontSize,
|
|
78
|
+
fontWeight: "600",
|
|
79
|
+
fill: wmColor,
|
|
80
|
+
angle,
|
|
81
|
+
selectable: false,
|
|
82
|
+
locked: true,
|
|
83
|
+
visible: true,
|
|
84
|
+
opacity: 0.14,
|
|
85
|
+
scaleX: 1,
|
|
86
|
+
scaleY: 1,
|
|
87
|
+
letterSpacing: 4
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return elements;
|
|
92
|
+
}
|
|
93
|
+
function injectWatermark(config) {
|
|
94
|
+
var _a, _b, _c;
|
|
95
|
+
const canvasWidth = ((_a = config.canvas) == null ? void 0 : _a.width) || 612;
|
|
96
|
+
const canvasHeight = ((_b = config.canvas) == null ? void 0 : _b.height) || 792;
|
|
97
|
+
const canvasBg = (_c = config.canvas) == null ? void 0 : _c.backgroundColor;
|
|
98
|
+
return {
|
|
99
|
+
...config,
|
|
100
|
+
pages: config.pages.map((page) => {
|
|
101
|
+
const pageBg = getPageBgColor(page, canvasBg);
|
|
102
|
+
const wmColor = getWatermarkColor(pageBg);
|
|
103
|
+
const watermarkElements = generateWatermarkElements(canvasWidth, canvasHeight, wmColor);
|
|
104
|
+
return {
|
|
105
|
+
...page,
|
|
106
|
+
children: [
|
|
107
|
+
...page.children || [],
|
|
108
|
+
...watermarkElements
|
|
109
|
+
]
|
|
110
|
+
};
|
|
111
|
+
})
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
exports.injectWatermark = injectWatermark;
|
|
115
|
+
//# sourceMappingURL=canvasWatermark-C7Jk6O8G.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasWatermark-C7Jk6O8G.cjs","sources":["../../../src/lib/canvasWatermark.ts"],"sourcesContent":["/**\n * Injects diagonal \"PixlDocs\" watermark text elements into a TemplateConfig's pages.\n * The watermark elements are rendered as actual canvas text objects,\n * making them impossible to remove via browser inspect.\n */\n\nimport type { TemplateConfig, CanvasElement } from '@/types/editor';\n\nconst WATERMARK_ID_PREFIX = '__wm_';\n\n/** Determine watermark text based on current domain */\nfunction getWatermarkText(): string {\n try {\n const host = window.location.hostname;\n if (host.includes('biomaker')) return 'biomaker.app';\n } catch {}\n return 'pixldocs.com';\n}\n\n/** Parse a hex/rgb color to luminance (0=black, 1=white) */\nfunction getLuminance(color: string | undefined): number {\n if (!color || color === 'transparent' || color === 'none') return 1; // assume light\n let r = 255, g = 255, b = 255;\n if (color.startsWith('#')) {\n const hex = color.slice(1);\n const full = hex.length === 3 ? hex.split('').map(c => c + c).join('') : hex;\n const n = parseInt(full, 16);\n r = (n >> 16) & 255;\n g = (n >> 8) & 255;\n b = n & 255;\n } else {\n const m = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (m) { r = +m[1]; g = +m[2]; b = +m[3]; }\n }\n return (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n}\n\n/** Pick watermark color that contrasts with page background */\nfunction getWatermarkColor(bgColor: string | undefined): string {\n const lum = getLuminance(bgColor);\n // Dark bg → light watermark, Light bg → dark watermark\n return lum < 0.45 ? '#cccccc' : '#808080';\n}\n\n/** Find the dominant background color of a page */\nfunction getPageBgColor(page: { children?: any[] }, canvasBg?: string): string | undefined {\n const children = page.children || [];\n for (const child of children) {\n if (child.type === 'shape' && child.fill && child.fill !== 'transparent' && child.fill !== 'none') {\n return child.fill;\n }\n // Check inside groups (background groups)\n if (child.children?.length) {\n for (const gc of child.children) {\n if (gc.type === 'shape' && gc.fill && gc.fill !== 'transparent' && gc.fill !== 'none') {\n return gc.fill;\n }\n }\n }\n }\n return canvasBg;\n}\n\n/** Generate watermark text elements for a given canvas size */\nfunction generateWatermarkElements(\n canvasWidth: number,\n canvasHeight: number,\n wmColor: string,\n): CanvasElement[] {\n const elements: CanvasElement[] = [];\n const text = getWatermarkText();\n const fontSize = 18;\n const angle = -30;\n // Adjust spacing based on text length to prevent overlap\n const estimatedTextWidth = text.length * fontSize * 0.55;\n const spacingX = Math.max(160, estimatedTextWidth + 30);\n const spacingY = 90;\n\n const startX = -canvasWidth * 0.3;\n const startY = -canvasHeight * 0.3;\n const endX = canvasWidth * 1.5;\n const endY = canvasHeight * 1.5;\n\n let idx = 0;\n for (let y = startY; y < endY; y += spacingY) {\n for (let x = startX; x < endX; x += spacingX) {\n elements.push({\n id: `${WATERMARK_ID_PREFIX}${idx++}`,\n type: 'text',\n left: x,\n top: y,\n width: 200,\n height: 40,\n text,\n fontFamily: 'Montserrat',\n fontSize,\n fontWeight: '600',\n fill: wmColor,\n angle,\n selectable: false,\n locked: true,\n visible: true,\n opacity: 0.14,\n scaleX: 1,\n scaleY: 1,\n letterSpacing: 4,\n } as CanvasElement);\n }\n }\n return elements;\n}\n\n/**\n * Returns a new config with watermark elements injected into every page.\n * The original config is NOT mutated.\n */\nexport function injectWatermark(config: TemplateConfig): TemplateConfig {\n const canvasWidth = config.canvas?.width || 612;\n const canvasHeight = config.canvas?.height || 792;\n const canvasBg = (config.canvas as any)?.backgroundColor;\n\n return {\n ...config,\n pages: config.pages.map((page) => {\n const pageBg = getPageBgColor(page as any, canvasBg);\n const wmColor = getWatermarkColor(pageBg);\n const watermarkElements = generateWatermarkElements(canvasWidth, canvasHeight, wmColor);\n return {\n ...page,\n children: [\n ...(page.children || []),\n ...watermarkElements,\n ],\n };\n }),\n };\n}\n\n/** Check if an element ID is a watermark element */\nexport function isWatermarkElement(id: string): boolean {\n return id.startsWith(WATERMARK_ID_PREFIX);\n}\n"],"names":[],"mappings":";;AAQA,MAAM,sBAAsB;AAG5B,SAAS,mBAA2B;AAClC,MAAI;AACF,UAAM,OAAO,OAAO,SAAS;AAC7B,QAAI,KAAK,SAAS,UAAU,EAAG,QAAO;AAAA,EACxC,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AAGA,SAAS,aAAa,OAAmC;AACvD,MAAI,CAAC,SAAS,UAAU,iBAAiB,UAAU,OAAQ,QAAO;AAClE,MAAI,IAAI,KAAK,IAAI,KAAK,IAAI;AAC1B,MAAI,MAAM,WAAW,GAAG,GAAG;AACzB,UAAM,MAAM,MAAM,MAAM,CAAC;AACzB,UAAM,OAAO,IAAI,WAAW,IAAI,IAAI,MAAM,EAAE,EAAE,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI;AACzE,UAAM,IAAI,SAAS,MAAM,EAAE;AAC3B,QAAK,KAAK,KAAM;AAChB,QAAK,KAAK,IAAK;AACf,QAAI,IAAI;AAAA,EACV,OAAO;AACL,UAAM,IAAI,MAAM,MAAM,gCAAgC;AACtD,QAAI,GAAG;AAAE,UAAI,CAAC,EAAE,CAAC;AAAG,UAAI,CAAC,EAAE,CAAC;AAAG,UAAI,CAAC,EAAE,CAAC;AAAA,IAAG;AAAA,EAC5C;AACA,UAAQ,QAAQ,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAC/C;AAGA,SAAS,kBAAkB,SAAqC;AAC9D,QAAM,MAAM,aAAa,OAAO;AAEhC,SAAO,MAAM,OAAO,YAAY;AAClC;AAGA,SAAS,eAAe,MAA4B,UAAuC;;AACzF,QAAM,WAAW,KAAK,YAAY,CAAA;AAClC,aAAW,SAAS,UAAU;AAC5B,QAAI,MAAM,SAAS,WAAW,MAAM,QAAQ,MAAM,SAAS,iBAAiB,MAAM,SAAS,QAAQ;AACjG,aAAO,MAAM;AAAA,IACf;AAEA,SAAI,WAAM,aAAN,mBAAgB,QAAQ;AAC1B,iBAAW,MAAM,MAAM,UAAU;AAC/B,YAAI,GAAG,SAAS,WAAW,GAAG,QAAQ,GAAG,SAAS,iBAAiB,GAAG,SAAS,QAAQ;AACrF,iBAAO,GAAG;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,0BACP,aACA,cACA,SACiB;AACjB,QAAM,WAA4B,CAAA;AAClC,QAAM,OAAO,iBAAA;AACb,QAAM,WAAW;AACjB,QAAM,QAAQ;AAEd,QAAM,qBAAqB,KAAK,SAAS,WAAW;AACpD,QAAM,WAAW,KAAK,IAAI,KAAK,qBAAqB,EAAE;AACtD,QAAM,WAAW;AAEjB,QAAM,SAAS,CAAC,cAAc;AAC9B,QAAM,SAAS,CAAC,eAAe;AAC/B,QAAM,OAAO,cAAc;AAC3B,QAAM,OAAO,eAAe;AAE5B,MAAI,MAAM;AACV,WAAS,IAAI,QAAQ,IAAI,MAAM,KAAK,UAAU;AAC5C,aAAS,IAAI,QAAQ,IAAI,MAAM,KAAK,UAAU;AAC5C,eAAS,KAAK;AAAA,QACZ,IAAI,GAAG,mBAAmB,GAAG,KAAK;AAAA,QAClC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA,YAAY;AAAA,QACZ,MAAM;AAAA,QACN;AAAA,QACA,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,eAAe;AAAA,MAAA,CACC;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,gBAAgB,QAAwC;;AACtE,QAAM,gBAAc,YAAO,WAAP,mBAAe,UAAS;AAC5C,QAAM,iBAAe,YAAO,WAAP,mBAAe,WAAU;AAC9C,QAAM,YAAY,YAAO,WAAP,mBAAuB;AAEzC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,OAAO,MAAM,IAAI,CAAC,SAAS;AAChC,YAAM,SAAS,eAAe,MAAa,QAAQ;AACnD,YAAM,UAAU,kBAAkB,MAAM;AACxC,YAAM,oBAAoB,0BAA0B,aAAa,cAAc,OAAO;AACtF,aAAO;AAAA,QACL,GAAG;AAAA,QACH,UAAU;AAAA,UACR,GAAI,KAAK,YAAY,CAAA;AAAA,UACrB,GAAG;AAAA,QAAA;AAAA,MACL;AAAA,IAEJ,CAAC;AAAA,EAAA;AAEL;;"}
|
package/dist/index.cjs
CHANGED
|
@@ -10093,7 +10093,7 @@ async function resolveTemplateData(options) {
|
|
|
10093
10093
|
const defaultData = template.default_data;
|
|
10094
10094
|
if (defaultData) config = applyFormDataSimple(config, defaultData);
|
|
10095
10095
|
if (formData && Object.keys(formData).length > 0) config = applyFormDataSimple(config, formData);
|
|
10096
|
-
return { config, templateName: template.name || "Untitled", templateId };
|
|
10096
|
+
return { config, templateName: template.name || "Untitled", templateId, price: template.price ?? 0 };
|
|
10097
10097
|
}
|
|
10098
10098
|
async function resolveFromForm(options) {
|
|
10099
10099
|
var _a, _b, _c, _d;
|
|
@@ -10234,7 +10234,8 @@ async function resolveFromForm(options) {
|
|
|
10234
10234
|
return {
|
|
10235
10235
|
config: resolvedConfig,
|
|
10236
10236
|
templateName: templateRow.name || "Untitled",
|
|
10237
|
-
templateId
|
|
10237
|
+
templateId,
|
|
10238
|
+
price: templateRow.price ?? 0
|
|
10238
10239
|
};
|
|
10239
10240
|
}
|
|
10240
10241
|
function flattenAll(nodes) {
|
|
@@ -10684,7 +10685,7 @@ class PixldocsRenderer {
|
|
|
10684
10685
|
* This is the primary external API for the package.
|
|
10685
10686
|
*/
|
|
10686
10687
|
async renderFromForm(options) {
|
|
10687
|
-
const { templateId, formSchemaId, sectionState, themeId, ...renderOpts } = options;
|
|
10688
|
+
const { templateId, formSchemaId, sectionState, themeId, watermark, ...renderOpts } = options;
|
|
10688
10689
|
const resolved = await resolveFromForm({
|
|
10689
10690
|
templateId,
|
|
10690
10691
|
formSchemaId,
|
|
@@ -10693,7 +10694,13 @@ class PixldocsRenderer {
|
|
|
10693
10694
|
supabaseUrl: this.config.supabaseUrl,
|
|
10694
10695
|
supabaseAnonKey: this.config.supabaseAnonKey
|
|
10695
10696
|
});
|
|
10696
|
-
|
|
10697
|
+
const shouldWatermark = watermark ?? resolved.price > 0;
|
|
10698
|
+
let configToRender = resolved.config;
|
|
10699
|
+
if (shouldWatermark) {
|
|
10700
|
+
const { injectWatermark } = await Promise.resolve().then(() => require("./canvasWatermark-C7Jk6O8G.cjs"));
|
|
10701
|
+
configToRender = injectWatermark(configToRender);
|
|
10702
|
+
}
|
|
10703
|
+
return this.renderAllPages(configToRender, renderOpts);
|
|
10697
10704
|
}
|
|
10698
10705
|
/**
|
|
10699
10706
|
* Convenience: fetch by ID with simple flat data and render.
|
|
@@ -10708,50 +10715,85 @@ class PixldocsRenderer {
|
|
|
10708
10715
|
return this.render(resolved.config, options);
|
|
10709
10716
|
}
|
|
10710
10717
|
// ─── Internal: render a page using the full PreviewCanvas engine ───
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10718
|
+
getExpectedImageCount(config, pageIndex) {
|
|
10719
|
+
const page = config.pages[pageIndex];
|
|
10720
|
+
if (!(page == null ? void 0 : page.children)) return 0;
|
|
10721
|
+
let count = 0;
|
|
10722
|
+
const walk = (nodes) => {
|
|
10723
|
+
for (const node of nodes) {
|
|
10724
|
+
if (!node || node.visible === false) continue;
|
|
10725
|
+
const src = typeof node.src === "string" ? node.src.trim() : "";
|
|
10726
|
+
const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
|
|
10727
|
+
if (node.type === "image" && (src || imageUrl)) count += 1;
|
|
10728
|
+
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
10729
|
+
walk(node.children);
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
};
|
|
10733
|
+
walk(page.children);
|
|
10734
|
+
return count;
|
|
10735
|
+
}
|
|
10736
|
+
waitForCanvasImages(container, expectedImageCount, maxWaitMs = 15e3, pollMs = 120) {
|
|
10717
10737
|
return new Promise((resolve) => {
|
|
10718
10738
|
const start = Date.now();
|
|
10739
|
+
let stableFrames = 0;
|
|
10740
|
+
const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
|
|
10741
|
+
const collectRenderableImages = (obj, seen) => {
|
|
10742
|
+
if (!obj || typeof obj !== "object") return;
|
|
10743
|
+
const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
|
|
10744
|
+
for (const candidate of candidates) {
|
|
10745
|
+
if (isRenderableImage(candidate)) {
|
|
10746
|
+
seen.add(candidate);
|
|
10747
|
+
} else if (candidate instanceof HTMLImageElement) {
|
|
10748
|
+
return false;
|
|
10749
|
+
}
|
|
10750
|
+
}
|
|
10751
|
+
const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
|
|
10752
|
+
for (const child of nested) {
|
|
10753
|
+
if (collectRenderableImages(child, seen) === false) return false;
|
|
10754
|
+
}
|
|
10755
|
+
return true;
|
|
10756
|
+
};
|
|
10757
|
+
const getFabricCanvas = () => {
|
|
10758
|
+
const registry2 = window.__fabricCanvasRegistry;
|
|
10759
|
+
if (registry2 instanceof Map) {
|
|
10760
|
+
for (const entry of registry2.values()) {
|
|
10761
|
+
const canvas = entry == null ? void 0 : entry.canvas;
|
|
10762
|
+
const el = (canvas == null ? void 0 : canvas.lowerCanvasEl) || (canvas == null ? void 0 : canvas.upperCanvasEl);
|
|
10763
|
+
if (el && container.contains(el)) return canvas;
|
|
10764
|
+
}
|
|
10765
|
+
}
|
|
10766
|
+
return null;
|
|
10767
|
+
};
|
|
10768
|
+
const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
10719
10769
|
const check = () => {
|
|
10720
|
-
const
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
for (const child of obj._objects) {
|
|
10737
|
-
const childEl = child._element || child._originalElement;
|
|
10738
|
-
if (childEl instanceof HTMLImageElement && !childEl.complete) {
|
|
10739
|
-
allLoaded = false;
|
|
10740
|
-
break;
|
|
10741
|
-
}
|
|
10742
|
-
}
|
|
10743
|
-
if (!allLoaded) break;
|
|
10744
|
-
}
|
|
10745
|
-
}
|
|
10770
|
+
const elapsed = Date.now() - start;
|
|
10771
|
+
const domImages = Array.from(container.querySelectorAll("img"));
|
|
10772
|
+
const allDomLoaded = domImages.every((img) => img.complete && img.naturalWidth > 0 && img.naturalHeight > 0);
|
|
10773
|
+
const fabricCanvas = getFabricCanvas();
|
|
10774
|
+
const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
|
|
10775
|
+
const renderableImages = /* @__PURE__ */ new Set();
|
|
10776
|
+
const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
|
|
10777
|
+
const actualImageCount = Math.max(domImages.length, renderableImages.size);
|
|
10778
|
+
!!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
|
|
10779
|
+
const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
|
|
10780
|
+
const ready = allDomLoaded && fabricReady && hasExpectedAssets;
|
|
10781
|
+
if (ready) {
|
|
10782
|
+
stableFrames += 1;
|
|
10783
|
+
if (stableFrames >= 2) {
|
|
10784
|
+
settle();
|
|
10785
|
+
return;
|
|
10746
10786
|
}
|
|
10787
|
+
} else {
|
|
10788
|
+
stableFrames = 0;
|
|
10747
10789
|
}
|
|
10748
|
-
if (
|
|
10749
|
-
|
|
10790
|
+
if (elapsed >= maxWaitMs) {
|
|
10791
|
+
settle();
|
|
10750
10792
|
return;
|
|
10751
10793
|
}
|
|
10752
10794
|
setTimeout(check, pollMs);
|
|
10753
10795
|
};
|
|
10754
|
-
setTimeout(check,
|
|
10796
|
+
setTimeout(check, 0);
|
|
10755
10797
|
});
|
|
10756
10798
|
}
|
|
10757
10799
|
getNormalizedGradientStops(gradient) {
|
|
@@ -10852,7 +10894,8 @@ class PixldocsRenderer {
|
|
|
10852
10894
|
container.remove();
|
|
10853
10895
|
};
|
|
10854
10896
|
const onReady = () => {
|
|
10855
|
-
this.
|
|
10897
|
+
const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
|
|
10898
|
+
this.waitForCanvasImages(container, expectedImageCount).then(() => {
|
|
10856
10899
|
try {
|
|
10857
10900
|
const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
|
|
10858
10901
|
if (!fabricCanvas) {
|