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,600 @@
1
+ import { h } from "@stencil/core";
2
+ import { Canvas, FabricObject, Control, Point } from "fabric";
3
+ import { setCanvasBackground, printAreaToPixelCorners, pixelCornersToPrintArea, defaultPrintArea } from "../../utils/canvas-helpers";
4
+ const CORNER_RADIUS = 7;
5
+ /**
6
+ * Custom Fabric.js object that renders a quadrilateral defined by 4 corner
7
+ * offsets from the bounding-box center. Top/bottom edges can be curved
8
+ * via `bulge` (-1 to 1) using quadratic Bezier curves.
9
+ */
10
+ class PrintAreaQuad extends FabricObject {
11
+ constructor(options) {
12
+ super(options);
13
+ this.cornerOffsets = options?.cornerOffsets ?? [
14
+ new Point(-50, -50), new Point(50, -50),
15
+ new Point(50, 50), new Point(-50, 50),
16
+ ];
17
+ this.bulge = options?.bulge ?? 0;
18
+ this.objectCaching = false;
19
+ }
20
+ _render(ctx) {
21
+ const [tl, tr, br, bl] = this.cornerOffsets;
22
+ ctx.beginPath();
23
+ ctx.moveTo(tl.x, tl.y);
24
+ if (this.bulge !== 0) {
25
+ // Curved top edge: control point at midpoint shifted by bulge
26
+ const topMidX = (tl.x + tr.x) / 2;
27
+ const topMidY = (tl.y + tr.y) / 2;
28
+ const bulgePixels = this.bulge * (this.height ?? 1);
29
+ ctx.quadraticCurveTo(topMidX, topMidY - bulgePixels, tr.x, tr.y);
30
+ }
31
+ else {
32
+ ctx.lineTo(tr.x, tr.y);
33
+ }
34
+ ctx.lineTo(br.x, br.y);
35
+ if (this.bulge !== 0) {
36
+ // Curved bottom edge: same direction as top for cylindrical appearance
37
+ const botMidX = (br.x + bl.x) / 2;
38
+ const botMidY = (br.y + bl.y) / 2;
39
+ const bulgePixels = this.bulge * (this.height ?? 1);
40
+ ctx.quadraticCurveTo(botMidX, botMidY - bulgePixels, bl.x, bl.y);
41
+ }
42
+ else {
43
+ ctx.lineTo(bl.x, bl.y);
44
+ }
45
+ ctx.closePath();
46
+ this._renderPaintInOrder(ctx);
47
+ }
48
+ /** Recompute left/top/width/height from absolute corner positions. */
49
+ recalcBounds() {
50
+ const center = this.getCenterPoint();
51
+ const abs = this.cornerOffsets.map(o => new Point(center.x + o.x, center.y + o.y));
52
+ const xs = abs.map(p => p.x);
53
+ const ys = abs.map(p => p.y);
54
+ const minX = Math.min(...xs);
55
+ const maxX = Math.max(...xs);
56
+ const minY = Math.min(...ys);
57
+ const maxY = Math.max(...ys);
58
+ const newCenterX = (minX + maxX) / 2;
59
+ const newCenterY = (minY + maxY) / 2;
60
+ this.cornerOffsets = abs.map(p => new Point(p.x - newCenterX, p.y - newCenterY));
61
+ this.set({
62
+ left: newCenterX,
63
+ top: newCenterY,
64
+ width: Math.max(maxX - minX, 1),
65
+ height: Math.max(maxY - minY, 1),
66
+ });
67
+ this.setCoords();
68
+ }
69
+ }
70
+ function buildCornerControl(index) {
71
+ return new Control({
72
+ x: 0,
73
+ y: 0,
74
+ sizeX: CORNER_RADIUS * 2,
75
+ sizeY: CORNER_RADIUS * 2,
76
+ touchSizeX: CORNER_RADIUS * 4,
77
+ touchSizeY: CORNER_RADIUS * 4,
78
+ cursorStyleHandler: () => 'move',
79
+ positionHandler: (_dim, finalMatrix, fabricObject) => {
80
+ const quad = fabricObject;
81
+ return new Point(quad.cornerOffsets[index].x, quad.cornerOffsets[index].y).transform(finalMatrix);
82
+ },
83
+ actionHandler: (_eventData, transformData, x, y) => {
84
+ const quad = transformData.target;
85
+ const center = quad.getCenterPoint();
86
+ quad.cornerOffsets[index] = new Point(x - center.x, y - center.y);
87
+ quad.recalcBounds();
88
+ return true;
89
+ },
90
+ render: (ctx, left, top) => {
91
+ ctx.save();
92
+ ctx.fillStyle = '#2563eb';
93
+ ctx.strokeStyle = '#ffffff';
94
+ ctx.lineWidth = 2;
95
+ ctx.beginPath();
96
+ ctx.arc(left, top, CORNER_RADIUS, 0, Math.PI * 2);
97
+ ctx.fill();
98
+ ctx.stroke();
99
+ ctx.restore();
100
+ },
101
+ });
102
+ }
103
+ function buildBulgeControl() {
104
+ const HANDLE_OFFSET_X = 24;
105
+ const TRACK_GAP = 4;
106
+ return new Control({
107
+ x: 0.5,
108
+ y: 0,
109
+ offsetX: HANDLE_OFFSET_X,
110
+ sizeX: CORNER_RADIUS * 2,
111
+ sizeY: CORNER_RADIUS * 2,
112
+ touchSizeX: CORNER_RADIUS * 4,
113
+ touchSizeY: CORNER_RADIUS * 4,
114
+ cursorStyleHandler: () => 'ns-resize',
115
+ positionHandler: (dim, finalMatrix, fabricObject) => {
116
+ const quad = fabricObject;
117
+ return new Point(0.5 * dim.x + HANDLE_OFFSET_X, -quad.bulge * dim.y / 2).transform(finalMatrix);
118
+ },
119
+ actionHandler: (_eventData, transformData, _x, y) => {
120
+ const quad = transformData.target;
121
+ const center = quad.getCenterPoint();
122
+ const halfH = (quad.height ?? 0) * (quad.scaleY ?? 1) / 2;
123
+ const bulge = Math.max(-1, Math.min(1, (center.y - y) / Math.max(halfH, 1)));
124
+ quad.bulge = bulge;
125
+ return true;
126
+ },
127
+ render: (ctx, left, top, _styleOverride, fabricObject) => {
128
+ const quad = fabricObject;
129
+ const halfH = ((quad.height ?? 0) * (quad.scaleY ?? 1)) / 2;
130
+ const angleRad = ((quad.angle ?? 0) * Math.PI) / 180;
131
+ const trackTopX = left + Math.sin(angleRad) * halfH;
132
+ const trackTopY = top - Math.cos(angleRad) * halfH;
133
+ const trackBotX = left - Math.sin(angleRad) * halfH;
134
+ const trackBotY = top + Math.cos(angleRad) * halfH;
135
+ ctx.save();
136
+ // Track line
137
+ ctx.strokeStyle = 'rgba(37, 99, 235, 0.3)';
138
+ ctx.lineWidth = 2;
139
+ ctx.setLineDash([3, 3]);
140
+ ctx.beginPath();
141
+ ctx.moveTo(trackTopX, trackTopY - TRACK_GAP);
142
+ ctx.lineTo(trackBotX, trackBotY + TRACK_GAP);
143
+ ctx.stroke();
144
+ ctx.setLineDash([]);
145
+ // Handle circle
146
+ ctx.fillStyle = '#2563eb';
147
+ ctx.strokeStyle = '#ffffff';
148
+ ctx.lineWidth = 2;
149
+ ctx.beginPath();
150
+ ctx.arc(left, top, CORNER_RADIUS, 0, Math.PI * 2);
151
+ ctx.fill();
152
+ ctx.stroke();
153
+ // Arrow up + "+" label
154
+ ctx.fillStyle = 'rgba(37, 99, 235, 0.5)';
155
+ ctx.beginPath();
156
+ ctx.moveTo(trackTopX, trackTopY - TRACK_GAP);
157
+ ctx.lineTo(trackTopX - 4, trackTopY - TRACK_GAP + 6);
158
+ ctx.lineTo(trackTopX + 4, trackTopY - TRACK_GAP + 6);
159
+ ctx.closePath();
160
+ ctx.fill();
161
+ ctx.font = 'bold 10px sans-serif';
162
+ ctx.textAlign = 'center';
163
+ ctx.fillText('+', trackTopX, trackTopY - TRACK_GAP - 4);
164
+ // Arrow down + "\u2212" label
165
+ ctx.beginPath();
166
+ ctx.moveTo(trackBotX, trackBotY + TRACK_GAP);
167
+ ctx.lineTo(trackBotX - 4, trackBotY + TRACK_GAP - 6);
168
+ ctx.lineTo(trackBotX + 4, trackBotY + TRACK_GAP - 6);
169
+ ctx.closePath();
170
+ ctx.fill();
171
+ ctx.fillText('\u2212', trackBotX, trackBotY + TRACK_GAP + 12);
172
+ ctx.restore();
173
+ },
174
+ });
175
+ }
176
+ export class WtpPrintAreaEditor {
177
+ el;
178
+ /** Product background image URL. */
179
+ productImage;
180
+ /** Canvas width in pixels. */
181
+ width = 800;
182
+ /** Canvas height in pixels. */
183
+ height = 600;
184
+ /** Current print area (relative 0-1 coordinates). */
185
+ printArea;
186
+ /** Fires when the print area rectangle is modified. */
187
+ wtpPrintAreaChange;
188
+ canvas;
189
+ canvasEl;
190
+ areaQuad;
191
+ /** Generation counter to discard stale background loads. */
192
+ bgLoadGen = 0;
193
+ componentDidLoad() {
194
+ this.initCanvas();
195
+ }
196
+ disconnectedCallback() {
197
+ ++this.bgLoadGen;
198
+ this.canvas?.dispose();
199
+ }
200
+ async onProductImageChange() {
201
+ if (this.canvas === undefined || this.productImage === undefined)
202
+ return;
203
+ await this.reloadCanvas();
204
+ }
205
+ async onSizeChange() {
206
+ if (this.canvas === undefined)
207
+ return;
208
+ await this.reloadCanvas();
209
+ }
210
+ onPrintAreaChange() {
211
+ this.syncQuadFromPrintArea();
212
+ }
213
+ /** Get the current print area as relative 0-1 coordinates. */
214
+ async getPrintArea() {
215
+ return this.readQuadAsPrintArea();
216
+ }
217
+ /** Set the print area and update the quad on canvas. */
218
+ async setPrintArea(printArea) {
219
+ this.printArea = printArea;
220
+ this.syncQuadFromPrintArea();
221
+ }
222
+ /** Create the Fabric Canvas once and perform the initial load. */
223
+ async initCanvas() {
224
+ if (this.canvasEl === undefined)
225
+ return;
226
+ this.canvas = new Canvas(this.canvasEl, {
227
+ width: this.width,
228
+ height: this.height,
229
+ backgroundColor: '#ffffff',
230
+ selection: false,
231
+ });
232
+ this.canvas.on('object:modified', (e) => {
233
+ if (e.target === this.areaQuad) {
234
+ this.emitPrintArea();
235
+ }
236
+ });
237
+ this.canvas.on('object:moving', (e) => {
238
+ if (e.target === this.areaQuad) {
239
+ this.clampQuadToBounds();
240
+ }
241
+ });
242
+ await this.reloadCanvas();
243
+ }
244
+ /**
245
+ * Clear the canvas and reload background + quad.
246
+ * Keeps the Fabric Canvas instance alive (no dispose/recreate) to avoid
247
+ * stale DOM state from async dispose in Fabric v7.
248
+ */
249
+ async reloadCanvas() {
250
+ if (this.canvas === undefined)
251
+ return;
252
+ const gen = ++this.bgLoadGen;
253
+ // Remove all objects (background image + quad)
254
+ this.areaQuad = undefined;
255
+ this.canvas.discardActiveObject();
256
+ for (const obj of this.canvas.getObjects().slice()) {
257
+ this.canvas.remove(obj);
258
+ }
259
+ // Reset internal state to bounding-box dimensions
260
+ this.canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
261
+ this.canvas.setDimensions({ width: this.width, height: this.height });
262
+ this.canvas.backgroundColor = '#ffffff';
263
+ this.canvas.requestRenderAll();
264
+ // Load product image (may auto-resize canvas in contain mode)
265
+ if (this.productImage !== undefined && this.productImage !== '') {
266
+ await setCanvasBackground(this.canvas, this.productImage);
267
+ }
268
+ if (gen !== this.bgLoadGen)
269
+ return;
270
+ this.createAreaQuad();
271
+ this.canvas.renderAll();
272
+ }
273
+ createAreaQuad() {
274
+ if (this.canvas === undefined)
275
+ return;
276
+ const pa = this.printArea ?? defaultPrintArea();
277
+ // Convert relative coords directly to canvas pixels (image-relative coordinates)
278
+ const cw = this.canvas.getWidth();
279
+ const ch = this.canvas.getHeight();
280
+ const [tl, tr, br, bl] = printAreaToPixelCorners(pa, cw, ch);
281
+ // Compute centroid and bounding box
282
+ const cx = (tl.x + tr.x + br.x + bl.x) / 4;
283
+ const cy = (tl.y + tr.y + br.y + bl.y) / 4;
284
+ const xs = [tl.x, tr.x, br.x, bl.x];
285
+ const ys = [tl.y, tr.y, br.y, bl.y];
286
+ const bbW = Math.max(Math.max(...xs) - Math.min(...xs), 1);
287
+ const bbH = Math.max(Math.max(...ys) - Math.min(...ys), 1);
288
+ this.areaQuad = new PrintAreaQuad({
289
+ left: cx,
290
+ top: cy,
291
+ width: bbW,
292
+ height: bbH,
293
+ cornerOffsets: [
294
+ new Point(tl.x - cx, tl.y - cy),
295
+ new Point(tr.x - cx, tr.y - cy),
296
+ new Point(br.x - cx, br.y - cy),
297
+ new Point(bl.x - cx, bl.y - cy),
298
+ ],
299
+ bulge: pa.bulge ?? 0,
300
+ originX: 'center',
301
+ originY: 'center',
302
+ fill: 'rgba(37, 99, 235, 0.15)',
303
+ stroke: '#2563eb',
304
+ strokeWidth: 2,
305
+ strokeDashArray: [6, 4],
306
+ // Disable standard resize/rotate controls
307
+ lockScalingX: true,
308
+ lockScalingY: true,
309
+ lockRotation: true,
310
+ hasRotatingPoint: false,
311
+ borderColor: '#2563eb',
312
+ });
313
+ // Replace all controls with corner handles + bulge
314
+ this.areaQuad.controls = {
315
+ corner0: buildCornerControl(0),
316
+ corner1: buildCornerControl(1),
317
+ corner2: buildCornerControl(2),
318
+ corner3: buildCornerControl(3),
319
+ bulgeHandle: buildBulgeControl(),
320
+ };
321
+ this.canvas.add(this.areaQuad);
322
+ this.canvas.renderAll();
323
+ }
324
+ syncQuadFromPrintArea() {
325
+ if (this.areaQuad === undefined || this.canvas === undefined)
326
+ return;
327
+ const pa = this.printArea ?? defaultPrintArea();
328
+ // Convert relative coords directly to canvas pixels (image-relative coordinates)
329
+ const cw = this.canvas.getWidth();
330
+ const ch = this.canvas.getHeight();
331
+ const [tl, tr, br, bl] = printAreaToPixelCorners(pa, cw, ch);
332
+ const cx = (tl.x + tr.x + br.x + bl.x) / 4;
333
+ const cy = (tl.y + tr.y + br.y + bl.y) / 4;
334
+ const xs = [tl.x, tr.x, br.x, bl.x];
335
+ const ys = [tl.y, tr.y, br.y, bl.y];
336
+ this.areaQuad.cornerOffsets = [
337
+ new Point(tl.x - cx, tl.y - cy),
338
+ new Point(tr.x - cx, tr.y - cy),
339
+ new Point(br.x - cx, br.y - cy),
340
+ new Point(bl.x - cx, bl.y - cy),
341
+ ];
342
+ this.areaQuad.bulge = pa.bulge ?? 0;
343
+ this.areaQuad.set({
344
+ left: cx,
345
+ top: cy,
346
+ width: Math.max(Math.max(...xs) - Math.min(...xs), 1),
347
+ height: Math.max(Math.max(...ys) - Math.min(...ys), 1),
348
+ scaleX: 1,
349
+ scaleY: 1,
350
+ });
351
+ this.areaQuad.setCoords();
352
+ this.canvas.renderAll();
353
+ }
354
+ clampQuadToBounds() {
355
+ if (this.areaQuad === undefined)
356
+ return;
357
+ const center = this.areaQuad.getCenterPoint();
358
+ const abs = this.areaQuad.cornerOffsets.map(o => ({
359
+ x: center.x + o.x,
360
+ y: center.y + o.y,
361
+ }));
362
+ // Find how much the quad exceeds canvas bounds
363
+ let dx = 0;
364
+ let dy = 0;
365
+ for (const p of abs) {
366
+ if (p.x < 0)
367
+ dx = Math.max(dx, -p.x);
368
+ if (p.x > this.canvas.getWidth())
369
+ dx = Math.min(dx, this.canvas.getWidth() - p.x);
370
+ if (p.y < 0)
371
+ dy = Math.max(dy, -p.y);
372
+ if (p.y > this.canvas.getHeight())
373
+ dy = Math.min(dy, this.canvas.getHeight() - p.y);
374
+ }
375
+ if (dx !== 0 || dy !== 0) {
376
+ this.areaQuad.set({
377
+ left: (this.areaQuad.left ?? 0) + dx,
378
+ top: (this.areaQuad.top ?? 0) + dy,
379
+ });
380
+ }
381
+ }
382
+ readQuadAsPrintArea() {
383
+ if (this.areaQuad === undefined) {
384
+ return this.printArea ?? defaultPrintArea();
385
+ }
386
+ const center = this.areaQuad.getCenterPoint();
387
+ const corners = [
388
+ { x: center.x + this.areaQuad.cornerOffsets[0].x, y: center.y + this.areaQuad.cornerOffsets[0].y },
389
+ { x: center.x + this.areaQuad.cornerOffsets[1].x, y: center.y + this.areaQuad.cornerOffsets[1].y },
390
+ { x: center.x + this.areaQuad.cornerOffsets[2].x, y: center.y + this.areaQuad.cornerOffsets[2].y },
391
+ { x: center.x + this.areaQuad.cornerOffsets[3].x, y: center.y + this.areaQuad.cornerOffsets[3].y },
392
+ ];
393
+ // Convert canvas pixels directly to 0-1 image-relative coordinates
394
+ const cw = this.canvas.getWidth();
395
+ const ch = this.canvas.getHeight();
396
+ return pixelCornersToPrintArea(corners, cw, ch, this.areaQuad.bulge);
397
+ }
398
+ emitPrintArea() {
399
+ const area = this.readQuadAsPrintArea();
400
+ this.printArea = area;
401
+ this.wtpPrintAreaChange.emit(area);
402
+ }
403
+ render() {
404
+ return (h("div", { key: 'fbeaeb8ac49780440ceb1bffa38689a23aeef66a', class: "wtp-print-area-editor" }, h("canvas", { key: 'b7e97909efaa17c7ffb4b089cf31e508e20c094e', ref: el => (this.canvasEl = el) })));
405
+ }
406
+ static get is() { return "wtp-print-area-editor"; }
407
+ static get encapsulation() { return "scoped"; }
408
+ static get originalStyleUrls() {
409
+ return {
410
+ "$": ["wtp-print-area-editor.scss"]
411
+ };
412
+ }
413
+ static get styleUrls() {
414
+ return {
415
+ "$": ["wtp-print-area-editor.css"]
416
+ };
417
+ }
418
+ static get properties() {
419
+ return {
420
+ "productImage": {
421
+ "type": "string",
422
+ "mutable": false,
423
+ "complexType": {
424
+ "original": "string | undefined",
425
+ "resolved": "string",
426
+ "references": {}
427
+ },
428
+ "required": false,
429
+ "optional": false,
430
+ "docs": {
431
+ "tags": [],
432
+ "text": "Product background image URL."
433
+ },
434
+ "getter": false,
435
+ "setter": false,
436
+ "reflect": false,
437
+ "attribute": "product-image"
438
+ },
439
+ "width": {
440
+ "type": "number",
441
+ "mutable": false,
442
+ "complexType": {
443
+ "original": "number",
444
+ "resolved": "number",
445
+ "references": {}
446
+ },
447
+ "required": false,
448
+ "optional": false,
449
+ "docs": {
450
+ "tags": [],
451
+ "text": "Canvas width in pixels."
452
+ },
453
+ "getter": false,
454
+ "setter": false,
455
+ "reflect": false,
456
+ "attribute": "width",
457
+ "defaultValue": "800"
458
+ },
459
+ "height": {
460
+ "type": "number",
461
+ "mutable": false,
462
+ "complexType": {
463
+ "original": "number",
464
+ "resolved": "number",
465
+ "references": {}
466
+ },
467
+ "required": false,
468
+ "optional": false,
469
+ "docs": {
470
+ "tags": [],
471
+ "text": "Canvas height in pixels."
472
+ },
473
+ "getter": false,
474
+ "setter": false,
475
+ "reflect": false,
476
+ "attribute": "height",
477
+ "defaultValue": "600"
478
+ },
479
+ "printArea": {
480
+ "type": "unknown",
481
+ "mutable": true,
482
+ "complexType": {
483
+ "original": "PrintArea | undefined",
484
+ "resolved": "PrintArea",
485
+ "references": {
486
+ "PrintArea": {
487
+ "location": "import",
488
+ "path": "../../types",
489
+ "id": "src/types/index.ts::PrintArea",
490
+ "referenceLocation": "PrintArea"
491
+ }
492
+ }
493
+ },
494
+ "required": false,
495
+ "optional": false,
496
+ "docs": {
497
+ "tags": [],
498
+ "text": "Current print area (relative 0-1 coordinates)."
499
+ },
500
+ "getter": false,
501
+ "setter": false
502
+ }
503
+ };
504
+ }
505
+ static get events() {
506
+ return [{
507
+ "method": "wtpPrintAreaChange",
508
+ "name": "wtpPrintAreaChange",
509
+ "bubbles": true,
510
+ "cancelable": true,
511
+ "composed": true,
512
+ "docs": {
513
+ "tags": [],
514
+ "text": "Fires when the print area rectangle is modified."
515
+ },
516
+ "complexType": {
517
+ "original": "PrintArea",
518
+ "resolved": "PrintArea",
519
+ "references": {
520
+ "PrintArea": {
521
+ "location": "import",
522
+ "path": "../../types",
523
+ "id": "src/types/index.ts::PrintArea",
524
+ "referenceLocation": "PrintArea"
525
+ }
526
+ }
527
+ }
528
+ }];
529
+ }
530
+ static get methods() {
531
+ return {
532
+ "getPrintArea": {
533
+ "complexType": {
534
+ "signature": "() => Promise<PrintArea>",
535
+ "parameters": [],
536
+ "references": {
537
+ "Promise": {
538
+ "location": "global",
539
+ "id": "global::Promise"
540
+ },
541
+ "PrintArea": {
542
+ "location": "import",
543
+ "path": "../../types",
544
+ "id": "src/types/index.ts::PrintArea",
545
+ "referenceLocation": "PrintArea"
546
+ }
547
+ },
548
+ "return": "Promise<PrintArea>"
549
+ },
550
+ "docs": {
551
+ "text": "Get the current print area as relative 0-1 coordinates.",
552
+ "tags": []
553
+ }
554
+ },
555
+ "setPrintArea": {
556
+ "complexType": {
557
+ "signature": "(printArea: PrintArea) => Promise<void>",
558
+ "parameters": [{
559
+ "name": "printArea",
560
+ "type": "PrintArea",
561
+ "docs": ""
562
+ }],
563
+ "references": {
564
+ "Promise": {
565
+ "location": "global",
566
+ "id": "global::Promise"
567
+ },
568
+ "PrintArea": {
569
+ "location": "import",
570
+ "path": "../../types",
571
+ "id": "src/types/index.ts::PrintArea",
572
+ "referenceLocation": "PrintArea"
573
+ }
574
+ },
575
+ "return": "Promise<void>"
576
+ },
577
+ "docs": {
578
+ "text": "Set the print area and update the quad on canvas.",
579
+ "tags": []
580
+ }
581
+ }
582
+ };
583
+ }
584
+ static get elementRef() { return "el"; }
585
+ static get watchers() {
586
+ return [{
587
+ "propName": "productImage",
588
+ "methodName": "onProductImageChange"
589
+ }, {
590
+ "propName": "width",
591
+ "methodName": "onSizeChange"
592
+ }, {
593
+ "propName": "height",
594
+ "methodName": "onSizeChange"
595
+ }, {
596
+ "propName": "printArea",
597
+ "methodName": "onPrintAreaChange"
598
+ }];
599
+ }
600
+ }
@@ -0,0 +1 @@
1
+ <!-- by TradingView --><svg width="56" height="56" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg"><path fill="#08954C" d="M0 0h56v56H0z"/><path d="M5.6 25C4.4 25 4 26.05 4 26.58c0 1.49 1.17 1.65 1.38 1.72.85.29 1.38.27 1.38 1.05 0 .48-.36.84-.77.84-.7 0-.91-.43-1-1.19H4v2h1v-.5c.15.31.6.5 1.07.5.93 0 1.71-.68 1.71-1.76 0-1.24-.78-1.55-1.47-1.78-.7-.23-1.31-.35-1.31-.9 0-.49.35-.77.64-.77.28 0 .5.12.69.31.17.18.3.44.35.74h.9V25h-.95v.48C6.36 25.18 6 25 5.6 25Zm5.07 0C9.45 25 8 25.94 8 27.96 8 29.67 9.22 31 10.69 31c1.11 0 2-.63 2.3-2h-.97c-.28.63-.57 1-1.33 1C9.6 30 9 29.09 9 28c0-1.11.62-2 1.69-2 .29 0 .59.06.83.24.24.17.43.44.5.76H13v-2h-1v.5c-.41-.44-1.16-.5-1.33-.5Zm2.84 0v1H14v4h-.5v1h2.03v-1H15v-1.62h2V30h-.54v1h2.06v-1H18v-4h.52v-1h-2.06v1H17v1.38h-2V26h.53v-1H13.5ZM20 25v1h.55l-1.27 4h-.43v1h1.83v-1h-.55l.36-1h1.54l.35 1h-.56v1h1.88v-1h-.38l-1.24-4h.55v-1H20Zm4.45 0v1H25v4h-.55v1h3.93v-2h-.8v1H26v-1.73h1.34v-.91H26V26h1.57v.65h.81V25h-3.93Zm4.88 0v1H30v4h-.67v1h2.21v-1H31v-1.73h1.33v-.91H31V26h1.4v.65h.93V25h-4ZM34 25v1h.5v4H34v1h2v-1h-.6v-1.73H37v-.91h-1.6V26H37v.65h1V25h-4Zm4.44 0v1H39v4h-.56v1H42v-2h-1v1h-1v-4h.57v-1h-2.13Zm4.06 0v1h.5v4h-.5v1H47v-2h-1v1h-2v-1.73h1.38v-.91H44V26h2v.65h1V25h-4.5Zm5.02 0v1H48v4h-.48v1h2.03v-1H49v-1.73h.54c.37 0 .88.04.88.58V31H52v-1h-.66v-1.22c0-.35-.04-.75-.62-.9v-.03c.53-.25.8-.62.8-1.29 0-.08.01-.45-.22-.79a1.81 1.81 0 0 0-1.58-.77h-2.2ZM49 26h.48c.48 0 1.11-.06 1.11.65 0 .77-.5.83-1 .83H49V26Zm-27.73.25h.01l.57 1.75h-1.17l.59-1.75Z" fill="#fff"/></svg>
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @fileoverview Entry point for the web-to-print component library.
3
+ *
4
+ * Use this file to export utilities, types, and constants.
5
+ * Components are consumed via the approaches outlined in the README.
6
+ */
7
+ export { DEFAULT_VALIDATION_CONFIG, DEFAULT_BG_REMOVAL_CONFIG } from './types';
8
+ export { exportProductPdf } from './utils/pdf-export';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { DEFAULT_VALIDATION_CONFIG, DEFAULT_BG_REMOVAL_CONFIG } from './logo';
2
+ export { DEFAULT_LOGO_UPLOAD_LABELS, DEFAULT_EDITOR_LABELS } from './labels';
@@ -0,0 +1,30 @@
1
+ // User-facing strings for components, overridable via the `labels` prop.
2
+ // Pass any subset; missing keys fall back to the English defaults below.
3
+ export const DEFAULT_LOGO_UPLOAD_LABELS = {
4
+ urlPlaceholder: 'https://example.com/logo.png',
5
+ urlSubmit: 'Fetch',
6
+ dividerText: 'or',
7
+ dropPromptText: 'Drag & drop your logo here or click to browse',
8
+ dropPromptHint: 'PNG, JPEG, SVG, TIFF, or AVIF',
9
+ uploadAriaLabel: 'Upload logo file',
10
+ removeAriaLabel: (fileName) => `Remove ${fileName}`,
11
+ urlErrorEmpty: 'Please enter a URL',
12
+ urlErrorInvalid: 'Invalid URL format',
13
+ urlErrorProtocol: 'Only HTTPS URLs are supported',
14
+ urlErrorHttp: (status, statusText) => `HTTP ${status}: ${statusText}`,
15
+ urlErrorNetwork: 'Could not fetch the image. The server may not allow cross-origin requests (CORS).',
16
+ urlErrorFetch: 'Failed to fetch image',
17
+ bgRemovalProcessing: 'Removing background...',
18
+ bgRemovalUseOriginal: 'Use Original',
19
+ bgRemovalUseRemoved: 'Without Background',
20
+ bgRemovalFailed: 'Failed',
21
+ rejectionDpiUnit: 'DPI',
22
+ };
23
+ export const DEFAULT_EDITOR_LABELS = {
24
+ addTextButton: 'Add Text',
25
+ addTextTooltip: 'Add text',
26
+ fontSelectTooltip: 'Font family',
27
+ colorPickerTooltip: 'Text color',
28
+ deleteButtonTooltip: 'Delete selected',
29
+ defaultText: 'New Text',
30
+ };
@@ -0,0 +1,13 @@
1
+ export const DEFAULT_VALIDATION_CONFIG = {
2
+ minDpi: 300,
3
+ maxFileSize: 50 * 1024 * 1024, // 50MB
4
+ minWidth: 100,
5
+ minHeight: 100,
6
+ allowedFormats: ['png', 'jpeg', 'svg', 'tiff', 'avif'],
7
+ };
8
+ export const DEFAULT_BG_REMOVAL_CONFIG = {
9
+ tolerance: 40,
10
+ minEdgeRatio: 0.3,
11
+ autoCrop: true,
12
+ autoCropPadding: 2,
13
+ };