web-to-print 0.1.0

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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  3. package/dist/cjs/canvas-helpers-A6rp5rPD.js +765 -0
  4. package/dist/cjs/index-IFGFRm-i.js +1649 -0
  5. package/dist/cjs/index.cjs.js +232 -0
  6. package/dist/cjs/loader.cjs.js +13 -0
  7. package/dist/cjs/logo-BUX-b45R.js +18 -0
  8. package/dist/cjs/web-to-print.cjs.js +25 -0
  9. package/dist/cjs/wtp-editor_2.cjs.entry.js +12386 -0
  10. package/dist/cjs/wtp-logo-renderer.cjs.entry.js +353 -0
  11. package/dist/cjs/wtp-print-area-editor.cjs.entry.js +431 -0
  12. package/dist/collection/collection-manifest.json +16 -0
  13. package/dist/collection/components/wtp-editor/wtp-editor.css +124 -0
  14. package/dist/collection/components/wtp-editor/wtp-editor.js +1114 -0
  15. package/dist/collection/components/wtp-logo-renderer/wtp-logo-renderer.css +30 -0
  16. package/dist/collection/components/wtp-logo-renderer/wtp-logo-renderer.js +455 -0
  17. package/dist/collection/components/wtp-logo-upload/wtp-logo-upload.css +428 -0
  18. package/dist/collection/components/wtp-logo-upload/wtp-logo-upload.js +573 -0
  19. package/dist/collection/components/wtp-print-area-editor/wtp-print-area-editor.css +20 -0
  20. package/dist/collection/components/wtp-print-area-editor/wtp-print-area-editor.js +600 -0
  21. package/dist/collection/examples/schaeffler--big.svg +1 -0
  22. package/dist/collection/index.js +8 -0
  23. package/dist/collection/types/editor.js +1 -0
  24. package/dist/collection/types/index.js +2 -0
  25. package/dist/collection/types/labels.js +30 -0
  26. package/dist/collection/types/logo.js +13 -0
  27. package/dist/collection/utils/background-removal.js +717 -0
  28. package/dist/collection/utils/canvas-helpers.js +380 -0
  29. package/dist/collection/utils/format-detection.js +48 -0
  30. package/dist/collection/utils/html-render-helpers.js +106 -0
  31. package/dist/collection/utils/image-preview.js +54 -0
  32. package/dist/collection/utils/logo-validation.js +141 -0
  33. package/dist/collection/utils/pdf-export.js +224 -0
  34. package/dist/components/index.d.ts +35 -0
  35. package/dist/components/index.js +1 -0
  36. package/dist/components/p-5qCsRzlt.js +1 -0
  37. package/dist/components/p-Bn9gR_8e.js +1 -0
  38. package/dist/components/p-D8pVJRuX.js +1 -0
  39. package/dist/components/wtp-editor.d.ts +11 -0
  40. package/dist/components/wtp-editor.js +1 -0
  41. package/dist/components/wtp-logo-renderer.d.ts +11 -0
  42. package/dist/components/wtp-logo-renderer.js +1 -0
  43. package/dist/components/wtp-logo-upload.d.ts +11 -0
  44. package/dist/components/wtp-logo-upload.js +1 -0
  45. package/dist/components/wtp-print-area-editor.d.ts +11 -0
  46. package/dist/components/wtp-print-area-editor.js +1 -0
  47. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  48. package/dist/esm/canvas-helpers-CK8OAq2J.js +748 -0
  49. package/dist/esm/index-CUetmLbL.js +1641 -0
  50. package/dist/esm/index.js +228 -0
  51. package/dist/esm/loader.js +11 -0
  52. package/dist/esm/logo-D8pVJRuX.js +15 -0
  53. package/dist/esm/web-to-print.js +21 -0
  54. package/dist/esm/wtp-editor_2.entry.js +12383 -0
  55. package/dist/esm/wtp-logo-renderer.entry.js +351 -0
  56. package/dist/esm/wtp-print-area-editor.entry.js +429 -0
  57. package/dist/index.cjs.js +1 -0
  58. package/dist/index.js +1 -0
  59. package/dist/types/components/wtp-editor/wtp-editor.d.ts +101 -0
  60. package/dist/types/components/wtp-logo-renderer/wtp-logo-renderer.d.ts +55 -0
  61. package/dist/types/components/wtp-logo-upload/wtp-logo-upload.d.ts +76 -0
  62. package/dist/types/components/wtp-print-area-editor/wtp-print-area-editor.d.ts +43 -0
  63. package/dist/types/components.d.ts +507 -0
  64. package/dist/types/index.d.ts +11 -0
  65. package/dist/types/stencil-public-runtime.d.ts +1860 -0
  66. package/dist/types/types/editor.d.ts +79 -0
  67. package/dist/types/types/index.d.ts +5 -0
  68. package/dist/types/types/labels.d.ts +30 -0
  69. package/dist/types/types/logo.d.ts +47 -0
  70. package/dist/types/utils/background-removal.d.ts +95 -0
  71. package/dist/types/utils/canvas-helpers.d.ts +60 -0
  72. package/dist/types/utils/format-detection.d.ts +4 -0
  73. package/dist/types/utils/html-render-helpers.d.ts +44 -0
  74. package/dist/types/utils/image-preview.d.ts +13 -0
  75. package/dist/types/utils/logo-validation.d.ts +2 -0
  76. package/dist/types/utils/pdf-export.d.ts +32 -0
  77. package/dist/web-to-print/index.esm.js +1 -0
  78. package/dist/web-to-print/p-611ec561.entry.js +1 -0
  79. package/dist/web-to-print/p-703e4c52.entry.js +1 -0
  80. package/dist/web-to-print/p-CK8OAq2J.js +1 -0
  81. package/dist/web-to-print/p-CUetmLbL.js +2 -0
  82. package/dist/web-to-print/p-D8pVJRuX.js +1 -0
  83. package/dist/web-to-print/p-DQuL1Twl.js +1 -0
  84. package/dist/web-to-print/p-b532777b.entry.js +1 -0
  85. package/dist/web-to-print/web-to-print.esm.js +1 -0
  86. package/loader/cdn.js +1 -0
  87. package/loader/index.cjs.js +1 -0
  88. package/loader/index.d.ts +24 -0
  89. package/loader/index.es2017.js +1 -0
  90. package/loader/index.js +2 -0
  91. package/package.json +68 -0
  92. package/readme.md +490 -0
@@ -0,0 +1,30 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :host {
8
+ font-family: var(--wtp-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
9
+ color: var(--wtp-color-text, #1e293b);
10
+ line-height: 1.5;
11
+ }
12
+
13
+ .wtp-logo-renderer {
14
+ display: inline-block;
15
+ position: relative;
16
+ overflow: hidden;
17
+ border: 1px solid var(--wtp-color-border, #e2e8f0);
18
+ border-radius: 8px;
19
+ }
20
+
21
+ .product-bg {
22
+ display: block;
23
+ width: 100%;
24
+ height: 100%;
25
+ object-fit: contain;
26
+ }
27
+
28
+ .logo-layer {
29
+ pointer-events: none;
30
+ }
@@ -0,0 +1,455 @@
1
+ import { h } from "@stencil/core";
2
+ import { fitLogoToPrintArea, printAreaToPixelCorners, warpImageForBulge, upscaleSvgDataUrl } from "../../utils/canvas-helpers";
3
+ import { loadImage, computeContainFit, centerOriginToTopLeft, getSvgIntrinsicSize, renderToCanvas } from "../../utils/html-render-helpers";
4
+ export class WtpLogoRenderer {
5
+ el;
6
+ /** Product background image URL. */
7
+ productImage;
8
+ /** Container width in pixels. */
9
+ width = 600;
10
+ /** Container height in pixels. */
11
+ height = 400;
12
+ /** Array of logos to place on the renderer. */
13
+ logos = [];
14
+ /** Background color. */
15
+ backgroundColor = '#ffffff';
16
+ /** Print area definition for auto-fitting logos (relative 0-1 coordinates). */
17
+ printArea;
18
+ /** Fires when the renderer has finished rendering all logos. */
19
+ wtpRenderComplete;
20
+ /** Fires when a rendering error occurs. */
21
+ wtpRenderError;
22
+ layers = [];
23
+ containerWidth = 0;
24
+ containerHeight = 0;
25
+ productImg;
26
+ componentWillLoad() {
27
+ this.computeLayout();
28
+ }
29
+ onProductImageChange() {
30
+ this.computeLayout();
31
+ }
32
+ onLogosChange() {
33
+ this.computeLayout();
34
+ }
35
+ onPrintAreaChange() {
36
+ this.computeLayout();
37
+ }
38
+ onSizeChange() {
39
+ this.computeLayout();
40
+ }
41
+ onBackgroundColorChange() {
42
+ // Background color is applied via inline style in render(), so Stencil
43
+ // re-renders automatically. We still need to re-emit the export data URL.
44
+ this.emitRenderComplete();
45
+ }
46
+ /** Export the rendered scene as a data URL image. */
47
+ async exportImage(format = 'png', quality = 1) {
48
+ const w = this.containerWidth || this.width;
49
+ const h = this.containerHeight || this.height;
50
+ const exportLayers = this.layers.map(l => ({
51
+ img: l.exportImg,
52
+ naturalWidth: l.naturalWidth,
53
+ naturalHeight: l.naturalHeight,
54
+ left: l.left,
55
+ top: l.top,
56
+ scaleX: l.scaleX,
57
+ scaleY: l.scaleY,
58
+ angle: l.angle,
59
+ skewX: l.skewX,
60
+ skewY: l.skewY,
61
+ }));
62
+ return renderToCanvas(w, h, this.backgroundColor, this.productImg, exportLayers, format, quality);
63
+ }
64
+ async computeLayout() {
65
+ try {
66
+ let cw = this.width;
67
+ let ch = this.height;
68
+ // Load product image and compute contain-fit dimensions
69
+ if (this.productImage !== undefined && this.productImage !== '') {
70
+ const img = await loadImage(this.productImage);
71
+ this.productImg = img;
72
+ const { fittedW, fittedH } = computeContainFit(this.width, this.height, img.naturalWidth, img.naturalHeight);
73
+ cw = fittedW;
74
+ ch = fittedH;
75
+ }
76
+ else {
77
+ this.productImg = undefined;
78
+ }
79
+ this.containerWidth = cw;
80
+ this.containerHeight = ch;
81
+ const newLayers = [];
82
+ for (const logo of this.logos) {
83
+ const displayUrl = logo.previewDataUrl ?? logo.dataUrl;
84
+ if (logo.transform !== undefined) {
85
+ // Explicit transform path
86
+ const exportImg = await loadImage(logo.dataUrl);
87
+ const nw = exportImg.naturalWidth;
88
+ const nh = exportImg.naturalHeight;
89
+ const { left, top } = centerOriginToTopLeft(logo.transform, nw, nh);
90
+ newLayers.push({
91
+ id: logo.id,
92
+ src: displayUrl,
93
+ exportImg,
94
+ naturalWidth: nw,
95
+ naturalHeight: nh,
96
+ left,
97
+ top,
98
+ scaleX: logo.transform.scaleX,
99
+ scaleY: logo.transform.scaleY,
100
+ angle: logo.transform.angle,
101
+ skewX: logo.transform.skewX ?? 0,
102
+ skewY: logo.transform.skewY ?? 0,
103
+ });
104
+ }
105
+ else if (this.printArea !== undefined) {
106
+ // Auto-fit path: determine logo dimensions, fit into print area
107
+ const hasBulge = this.printArea.bulge !== undefined && this.printArea.bulge !== 0;
108
+ let logoW;
109
+ let logoH;
110
+ let displaySrc = displayUrl;
111
+ if (hasBulge) {
112
+ // Bulge path: upscale SVG first (rasters pass through unchanged), then
113
+ // compute fit from actual pixel dimensions — matches the old Fabric.js flow
114
+ // where FabricImage.fromURL loaded the upscaled image before fitLogoToPrintArea.
115
+ const rendererMaxSize = Math.round(Math.max(cw, ch) * 2);
116
+ const { dataUrl: processedUrl } = upscaleSvgDataUrl(logo.dataUrl, rendererMaxSize);
117
+ const warpSrc = await loadImage(processedUrl);
118
+ logoW = warpSrc.naturalWidth;
119
+ logoH = warpSrc.naturalHeight;
120
+ const transform = fitLogoToPrintArea(logoW, logoH, this.printArea, cw, ch);
121
+ const [tl, tr, br, bl] = printAreaToPixelCorners(this.printArea, cw, ch);
122
+ const leftH = Math.hypot(bl.x - tl.x, bl.y - tl.y);
123
+ const rightH = Math.hypot(br.x - tr.x, br.y - tr.y);
124
+ const avgHeight = (leftH + rightH) / 2;
125
+ const warped = warpImageForBulge(warpSrc, this.printArea.bulge, avgHeight, transform.scaleX);
126
+ displaySrc = warped.toDataURL('image/png');
127
+ // Warped canvas may be taller due to displacement padding
128
+ logoW = warped.width;
129
+ logoH = warped.height;
130
+ const exportImg = await loadImage(displaySrc);
131
+ const { left, top } = centerOriginToTopLeft(transform, logoW, logoH);
132
+ newLayers.push({
133
+ id: logo.id,
134
+ src: displaySrc,
135
+ exportImg,
136
+ naturalWidth: logoW,
137
+ naturalHeight: logoH,
138
+ left,
139
+ top,
140
+ scaleX: transform.scaleX,
141
+ scaleY: transform.scaleY,
142
+ angle: transform.angle,
143
+ skewX: 0,
144
+ skewY: 0,
145
+ });
146
+ }
147
+ else {
148
+ // No bulge: use intrinsic SVG dimensions (vector quality in <img>)
149
+ // or raster naturalWidth/Height
150
+ const svgSize = getSvgIntrinsicSize(logo.dataUrl);
151
+ if (svgSize !== null) {
152
+ logoW = svgSize.width;
153
+ logoH = svgSize.height;
154
+ }
155
+ else {
156
+ const tempImg = await loadImage(logo.dataUrl);
157
+ logoW = tempImg.naturalWidth;
158
+ logoH = tempImg.naturalHeight;
159
+ }
160
+ const transform = fitLogoToPrintArea(logoW, logoH, this.printArea, cw, ch);
161
+ const exportImg = await loadImage(displaySrc);
162
+ const { left, top } = centerOriginToTopLeft(transform, logoW, logoH);
163
+ newLayers.push({
164
+ id: logo.id,
165
+ src: displaySrc,
166
+ exportImg,
167
+ naturalWidth: logoW,
168
+ naturalHeight: logoH,
169
+ left,
170
+ top,
171
+ scaleX: transform.scaleX,
172
+ scaleY: transform.scaleY,
173
+ angle: transform.angle,
174
+ skewX: 0,
175
+ skewY: 0,
176
+ });
177
+ }
178
+ }
179
+ }
180
+ this.layers = newLayers;
181
+ this.emitRenderComplete();
182
+ }
183
+ catch (err) {
184
+ const message = err instanceof Error ? err.message : 'Unknown render error';
185
+ this.wtpRenderError.emit({ message });
186
+ }
187
+ }
188
+ async emitRenderComplete() {
189
+ try {
190
+ const dataUrl = await this.exportImage('png');
191
+ this.wtpRenderComplete.emit({ dataUrl });
192
+ }
193
+ catch {
194
+ // Export may fail if called before layout is ready; ignore silently
195
+ }
196
+ }
197
+ render() {
198
+ const renderW = this.containerWidth || this.width;
199
+ const renderH = this.containerHeight || this.height;
200
+ return (h("div", { key: '3a720dae0cd0ab070a90b1f20dff9adb699b0ba5', class: "wtp-logo-renderer", style: {
201
+ width: `${renderW}px`,
202
+ height: `${renderH}px`,
203
+ backgroundColor: this.backgroundColor,
204
+ } }, this.productImage !== undefined && this.productImage !== '' && (h("img", { key: '88b50d0a4fd320a935bd7008ef3ddfee1644cbab', class: "product-bg", src: this.productImage, alt: "" })), this.layers.map(layer => (h("img", { key: layer.id, class: "logo-layer", src: layer.src, alt: "", style: {
205
+ position: 'absolute',
206
+ left: `${layer.left}px`,
207
+ top: `${layer.top}px`,
208
+ width: `${layer.naturalWidth}px`,
209
+ height: `${layer.naturalHeight}px`,
210
+ transformOrigin: 'center',
211
+ transform: `rotate(${layer.angle}deg) skewX(${layer.skewX}deg) skewY(${layer.skewY}deg) scale(${layer.scaleX}, ${layer.scaleY})`,
212
+ } })))));
213
+ }
214
+ static get is() { return "wtp-logo-renderer"; }
215
+ static get encapsulation() { return "scoped"; }
216
+ static get originalStyleUrls() {
217
+ return {
218
+ "$": ["wtp-logo-renderer.scss"]
219
+ };
220
+ }
221
+ static get styleUrls() {
222
+ return {
223
+ "$": ["wtp-logo-renderer.css"]
224
+ };
225
+ }
226
+ static get properties() {
227
+ return {
228
+ "productImage": {
229
+ "type": "string",
230
+ "mutable": false,
231
+ "complexType": {
232
+ "original": "string | undefined",
233
+ "resolved": "string",
234
+ "references": {}
235
+ },
236
+ "required": false,
237
+ "optional": false,
238
+ "docs": {
239
+ "tags": [],
240
+ "text": "Product background image URL."
241
+ },
242
+ "getter": false,
243
+ "setter": false,
244
+ "reflect": false,
245
+ "attribute": "product-image"
246
+ },
247
+ "width": {
248
+ "type": "number",
249
+ "mutable": false,
250
+ "complexType": {
251
+ "original": "number",
252
+ "resolved": "number",
253
+ "references": {}
254
+ },
255
+ "required": false,
256
+ "optional": false,
257
+ "docs": {
258
+ "tags": [],
259
+ "text": "Container width in pixels."
260
+ },
261
+ "getter": false,
262
+ "setter": false,
263
+ "reflect": false,
264
+ "attribute": "width",
265
+ "defaultValue": "600"
266
+ },
267
+ "height": {
268
+ "type": "number",
269
+ "mutable": false,
270
+ "complexType": {
271
+ "original": "number",
272
+ "resolved": "number",
273
+ "references": {}
274
+ },
275
+ "required": false,
276
+ "optional": false,
277
+ "docs": {
278
+ "tags": [],
279
+ "text": "Container height in pixels."
280
+ },
281
+ "getter": false,
282
+ "setter": false,
283
+ "reflect": false,
284
+ "attribute": "height",
285
+ "defaultValue": "400"
286
+ },
287
+ "logos": {
288
+ "type": "unknown",
289
+ "mutable": false,
290
+ "complexType": {
291
+ "original": "PlacedLogo[]",
292
+ "resolved": "PlacedLogo[]",
293
+ "references": {
294
+ "PlacedLogo": {
295
+ "location": "import",
296
+ "path": "../../types",
297
+ "id": "src/types/index.ts::PlacedLogo",
298
+ "referenceLocation": "PlacedLogo"
299
+ }
300
+ }
301
+ },
302
+ "required": false,
303
+ "optional": false,
304
+ "docs": {
305
+ "tags": [],
306
+ "text": "Array of logos to place on the renderer."
307
+ },
308
+ "getter": false,
309
+ "setter": false,
310
+ "defaultValue": "[]"
311
+ },
312
+ "backgroundColor": {
313
+ "type": "string",
314
+ "mutable": false,
315
+ "complexType": {
316
+ "original": "string",
317
+ "resolved": "string",
318
+ "references": {}
319
+ },
320
+ "required": false,
321
+ "optional": false,
322
+ "docs": {
323
+ "tags": [],
324
+ "text": "Background color."
325
+ },
326
+ "getter": false,
327
+ "setter": false,
328
+ "reflect": false,
329
+ "attribute": "background-color",
330
+ "defaultValue": "'#ffffff'"
331
+ },
332
+ "printArea": {
333
+ "type": "unknown",
334
+ "mutable": true,
335
+ "complexType": {
336
+ "original": "PrintArea | undefined",
337
+ "resolved": "PrintArea",
338
+ "references": {
339
+ "PrintArea": {
340
+ "location": "import",
341
+ "path": "../../types",
342
+ "id": "src/types/index.ts::PrintArea",
343
+ "referenceLocation": "PrintArea"
344
+ }
345
+ }
346
+ },
347
+ "required": false,
348
+ "optional": false,
349
+ "docs": {
350
+ "tags": [],
351
+ "text": "Print area definition for auto-fitting logos (relative 0-1 coordinates)."
352
+ },
353
+ "getter": false,
354
+ "setter": false
355
+ }
356
+ };
357
+ }
358
+ static get states() {
359
+ return {
360
+ "layers": {},
361
+ "containerWidth": {},
362
+ "containerHeight": {}
363
+ };
364
+ }
365
+ static get events() {
366
+ return [{
367
+ "method": "wtpRenderComplete",
368
+ "name": "wtpRenderComplete",
369
+ "bubbles": true,
370
+ "cancelable": true,
371
+ "composed": true,
372
+ "docs": {
373
+ "tags": [],
374
+ "text": "Fires when the renderer has finished rendering all logos."
375
+ },
376
+ "complexType": {
377
+ "original": "{ dataUrl: string }",
378
+ "resolved": "{ dataUrl: string; }",
379
+ "references": {}
380
+ }
381
+ }, {
382
+ "method": "wtpRenderError",
383
+ "name": "wtpRenderError",
384
+ "bubbles": true,
385
+ "cancelable": true,
386
+ "composed": true,
387
+ "docs": {
388
+ "tags": [],
389
+ "text": "Fires when a rendering error occurs."
390
+ },
391
+ "complexType": {
392
+ "original": "{ message: string }",
393
+ "resolved": "{ message: string; }",
394
+ "references": {}
395
+ }
396
+ }];
397
+ }
398
+ static get methods() {
399
+ return {
400
+ "exportImage": {
401
+ "complexType": {
402
+ "signature": "(format?: \"png\" | \"jpeg\", quality?: number) => Promise<string>",
403
+ "parameters": [{
404
+ "name": "format",
405
+ "type": "\"png\" | \"jpeg\"",
406
+ "docs": ""
407
+ }, {
408
+ "name": "quality",
409
+ "type": "number",
410
+ "docs": ""
411
+ }],
412
+ "references": {
413
+ "Promise": {
414
+ "location": "global",
415
+ "id": "global::Promise"
416
+ },
417
+ "RenderLayer": {
418
+ "location": "import",
419
+ "path": "../../utils/html-render-helpers",
420
+ "id": "src/utils/html-render-helpers.ts::RenderLayer",
421
+ "referenceLocation": "RenderLayer"
422
+ }
423
+ },
424
+ "return": "Promise<string>"
425
+ },
426
+ "docs": {
427
+ "text": "Export the rendered scene as a data URL image.",
428
+ "tags": []
429
+ }
430
+ }
431
+ };
432
+ }
433
+ static get elementRef() { return "el"; }
434
+ static get watchers() {
435
+ return [{
436
+ "propName": "productImage",
437
+ "methodName": "onProductImageChange"
438
+ }, {
439
+ "propName": "logos",
440
+ "methodName": "onLogosChange"
441
+ }, {
442
+ "propName": "printArea",
443
+ "methodName": "onPrintAreaChange"
444
+ }, {
445
+ "propName": "width",
446
+ "methodName": "onSizeChange"
447
+ }, {
448
+ "propName": "height",
449
+ "methodName": "onSizeChange"
450
+ }, {
451
+ "propName": "backgroundColor",
452
+ "methodName": "onBackgroundColorChange"
453
+ }];
454
+ }
455
+ }