hbf0421 commited on
Commit
ec4dbff
·
verified ·
1 Parent(s): 3e7b27c

Promote version 00280b6 to main

Browse files

Promoted commit 00280b6865beec9d88db8b33e7086d07f5f73caf to main branch

Files changed (3) hide show
  1. index.html +3 -3
  2. script.js +193 -326
  3. style.css +7 -33
index.html CHANGED
@@ -23,8 +23,8 @@
23
  <i data-feather="upload" class="w-12 h-12 mx-auto text-gray-400 mb-3"></i>
24
  <p class="text-gray-600 mb-2">Drag & drop your image here</p>
25
  <p class="text-sm text-gray-500 mb-4">or</p>
26
- <input type="file" id="imageUpload" accept="image/jpeg,image/png,image/gif" class="hidden">
27
- <button id="uploadBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors">
28
  Select Image
29
  </button>
30
  </div>
@@ -99,7 +99,7 @@
99
  <i data-feather="printer" class="inline mr-2"></i> Print All Pages
100
  </button>
101
  <button id="downloadAllBtn" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-lg transition-colors">
102
- <i data-feather="download" class="inline mr-2"></i> Download All as PDF11
103
  </button>
104
  <button id="resetBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-medium py-2 px-4 rounded-lg transition-colors">
105
  <i data-feather="refresh-cw" class="inline mr-2"></i> Start Over
 
23
  <i data-feather="upload" class="w-12 h-12 mx-auto text-gray-400 mb-3"></i>
24
  <p class="text-gray-600 mb-2">Drag & drop your image here</p>
25
  <p class="text-sm text-gray-500 mb-4">or</p>
26
+ <input type="file" id="imageUpload" accept="image/*" class="hidden">
27
+ <button id="uploadBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors">
28
  Select Image
29
  </button>
30
  </div>
 
99
  <i data-feather="printer" class="inline mr-2"></i> Print All Pages
100
  </button>
101
  <button id="downloadAllBtn" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-lg transition-colors">
102
+ <i data-feather="download" class="inline mr-2"></i> Download All as PDF
103
  </button>
104
  <button id="resetBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-medium py-2 px-4 rounded-lg transition-colors">
105
  <i data-feather="refresh-cw" class="inline mr-2"></i> Start Over
script.js CHANGED
@@ -32,22 +32,12 @@ let uploadedImage = null;
32
  // Functions
33
  function handleImageUpload(e) {
34
  const file = e.target.files[0];
35
- if (file) {
36
- // Check if file is an image
37
- if (!file.type.match('image.*')) {
38
- alert('Please select a valid image file (JPEG, PNG, GIF, etc.)');
39
- return;
40
- }
41
-
42
- // Reset any previous upload
43
- uploadedImage = null;
44
- generateBtn.disabled = true;
45
-
46
- // Process the new image
47
  processImageFile(file);
48
  }
49
  }
50
- function handleDragOver(e) {
 
51
  e.preventDefault();
52
  e.stopPropagation();
53
  dropZone.classList.add('drag-over');
@@ -69,376 +59,253 @@ function handleDragOver(e) {
69
  processImageFile(file);
70
  }
71
  }
 
72
  function processImageFile(file) {
73
  const reader = new FileReader();
74
- reader.onloadstart = function() {
75
- previewContainer.innerHTML = '<div class="text-center"><i data-feather="loader" class="w-8 h-8 mx-auto animate-spin text-gray-400"></i><p class="text-gray-600 mt-2">Loading image...</p></div>';
76
- feather.replace();
77
- };
78
-
79
  reader.onload = function(e) {
80
- previewContainer.innerHTML = ''; // Clear loading message
81
-
82
  uploadedImage = new Image();
83
- uploadedImage.crossOrigin = 'Anonymous'; // Handle CORS if needed
84
-
85
  uploadedImage.onload = function() {
86
- // Create canvas for preview
87
- const canvas = document.createElement('canvas');
88
- const ctx = canvas.getContext('2d');
89
-
90
- // Calculate dimensions to fit in preview area
91
- const maxWidth = 400;
92
- const maxHeight = 300;
93
- let width = uploadedImage.width;
94
- let height = uploadedImage.height;
95
-
96
- if (width > maxWidth) {
97
- const ratio = maxWidth / width;
98
- width = maxWidth;
99
- height = height * ratio;
100
- }
101
-
102
- if (height > maxHeight) {
103
- const ratio = maxHeight / height;
104
- height = maxHeight;
105
- width = width * ratio;
106
- }
107
-
108
- canvas.width = width;
109
- canvas.height = height;
110
-
111
- // Draw image
112
- ctx.drawImage(uploadedImage, 0, 0, width, height);
113
-
114
- // Add canvas to preview container
115
- canvas.className = 'max-w-full h-auto border border-gray-200 rounded';
116
- previewContainer.appendChild(canvas);
117
-
118
- // Enable generate button
119
  generateBtn.disabled = false;
120
- feather.replace();
121
  };
122
-
123
- uploadedImage.onerror = function() {
124
- previewContainer.innerHTML = '<div class="text-center text-red-500"><i data-feather="alert-triangle" class="w-8 h-8 mx-auto"></i><p class="mt-2">Error loading image. Please try another file.</p></div>';
125
- generateBtn.disabled = true;
126
- feather.replace();
127
- };
128
-
129
  uploadedImage.src = e.target.result;
130
- imageUpload.value = ''; // Reset input to allow same file to be selected again
131
- };
132
-
133
- reader.onerror = function() {
134
- previewContainer.innerHTML = '<div class="text-center text-red-500"><i data-feather="alert-triangle" class="w-8 h-8 mx-auto"></i><p class="mt-2">Error reading file. Please try again.</p></div>';
135
- generateBtn.disabled = true;
136
- feather.replace();
137
  };
138
-
139
  reader.readAsDataURL(file);
140
  }
141
- }
142
-
143
  function displayPreview(image) {
144
- previewContainer.innerHTML = '';
145
 
146
  const canvas = document.createElement('canvas');
147
- canvas.className = 'max-w-full h-auto border border-gray-200 rounded';
148
  const ctx = canvas.getContext('2d');
149
-
150
- // Calculate dimensions to fit in preview area
151
  const maxWidth = 400;
152
  const maxHeight = 300;
153
  let width = image.width;
154
  let height = image.height;
155
 
156
  if (width > maxWidth) {
157
- const ratio = maxWidth / width;
158
  width = maxWidth;
159
- height = height * ratio;
160
  }
161
-
162
  if (height > maxHeight) {
163
- const ratio = maxHeight / height;
164
  height = maxHeight;
165
- width = width * ratio;
166
  }
167
 
168
  canvas.width = width;
169
  canvas.height = height;
170
-
171
- // Draw image
172
  ctx.drawImage(image, 0, 0, width, height);
173
 
174
- // Draw grid lines only if image is loaded successfully
175
- try {
176
- const pages = parseInt(pageCount.value);
177
- const gridSize = Math.sqrt(pages);
178
- const cellWidth = width / gridSize;
179
- const cellHeight = height / gridSize;
180
-
181
- ctx.strokeStyle = 'rgba(255, 0, 0, 0.7)';
182
- ctx.lineWidth = 2;
183
-
184
- // Draw vertical lines
185
- for (let i = 1; i < gridSize; i++) {
186
- ctx.beginPath();
187
- ctx.moveTo(i * cellWidth, 0);
188
- ctx.lineTo(i * cellWidth, height);
189
- ctx.stroke();
190
- }
191
-
192
- // Draw horizontal lines
193
- for (let i = 1; i < gridSize; i++) {
194
- ctx.beginPath();
195
- ctx.moveTo(0, i * cellHeight);
196
- ctx.lineTo(width, i * cellHeight);
197
- ctx.stroke();
198
- }
199
- } catch (error) {
200
- console.error('Error drawing grid:', error);
201
- }
202
-
203
  previewContainer.appendChild(canvas);
204
-
205
- // Update grid whenever page count changes
206
- pageCount.addEventListener('change', function() {
207
- if (uploadedImage) {
208
- displayPreview(uploadedImage);
209
- }
210
- });
211
  }
212
- function generatePosterPages() {
213
- if (!uploadedImage) return;
214
-
215
- posterPages.innerHTML = '';
216
- resultsSection.classList.remove('hidden');
217
 
218
- const pages = parseInt(pageCount.value);
219
- const gridSize = Math.sqrt(pages);
220
- const orient = orientation.value;
221
- const overlapXPx = (parseInt(overlapX.value) / 25.4) * 96; // Convert mm to pixels (96dpi)
222
- const overlapYPx = (parseInt(overlapY.value) / 25.4) * 96; // Convert mm to pixels (96dpi)
223
- const showGuides = showGrid.checked;
224
- const centerOverlaps = centerOverlap.checked;
225
-
226
- // Base segment dimensions without overlap
227
- const baseSegmentWidth = uploadedImage.width / gridSize;
228
- const baseSegmentHeight = uploadedImage.height / gridSize;
229
-
230
- // Create a canvas for each page
231
- for (let row = 0; row < gridSize; row++) {
232
- for (let col = 0; col < gridSize; col++) {
233
- const pageNumber = row * gridSize + col + 1;
234
- const pageDiv = document.createElement('div');
235
- pageDiv.className = 'poster-page relative';
236
-
237
- const canvas = document.createElement('canvas');
238
-
239
- // Set canvas dimensions based on orientation
240
- if (orient === 'landscape') {
241
- canvas.width = 842; // A4 landscape width in pixels (297mm)
242
- canvas.height = 595; // A4 landscape height in pixels (210mm)
243
- } else if (orient === 'diagonal') {
244
- // For diagonal, use portrait dimensions but we'll rotate
245
- canvas.width = 595;
246
- canvas.height = 842;
247
- } else {
248
- // Portrait by default
249
- canvas.width = 595; // A4 portrait width in pixels (210mm)
250
- canvas.height = 842; // A4 portrait height in pixels (297mm)
251
- }
252
-
253
- const ctx = canvas.getContext('2d');
254
-
255
- // Calculate source coordinates with overlap
256
- let sx, sy, sw, sh;
257
-
258
- if (centerOverlaps) {
259
- // Equal overlap on all sides
260
- sx = Math.max(0, col * baseSegmentWidth - overlapXPx);
261
- sy = Math.max(0, row * baseSegmentHeight - overlapYPx);
262
 
263
- // Calculate width with overlap
264
- if (col === 0) {
265
- sw = baseSegmentWidth + overlapXPx;
266
- } else if (col === gridSize-1) {
267
- sw = uploadedImage.width - sx;
268
  } else {
269
- sw = baseSegmentWidth + 2 * overlapXPx;
 
270
  }
271
 
272
- // Calculate height with overlap
273
- if (row === 0) {
274
- sh = baseSegmentHeight + overlapYPx;
275
- } else if (row === gridSize-1) {
276
- sh = uploadedImage.height - sy;
 
 
 
 
 
 
 
 
 
277
  } else {
278
- sh = baseSegmentHeight + 2 * overlapYPx;
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  }
280
- } else {
281
- // Traditional overlaps - overlap only right and bottom
282
- sx = col * baseSegmentWidth;
283
- sy = row * baseSegmentHeight;
284
 
285
- // Calculate width with overlap
286
- if (col === gridSize-1) {
287
- sw = uploadedImage.width - sx;
288
- } else {
289
- sw = baseSegmentWidth + overlapXPx;
 
 
 
 
290
  }
291
 
292
- // Calculate height with overlap
293
- if (row === gridSize-1) {
294
- sh = uploadedImage.height - sy;
 
 
 
 
 
 
 
 
 
 
 
 
295
  } else {
296
- sh = baseSegmentHeight + overlapYPx;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  }
298
- }
299
- // Ensure we don't go out of bounds
300
- sx = Math.max(0, sx);
301
- sy = Math.max(0, sy);
302
- sw = Math.min(sw, uploadedImage.width - sx);
303
- sh = Math.min(sh, uploadedImage.height - sy);
304
-
305
- // Clear canvas
306
- ctx.clearRect(0, 0, canvas.width, canvas.height);
307
-
308
- // Draw the image segment
309
- ctx.save();
310
-
311
- if (orient === 'diagonal') {
312
- // For diagonal orientation, rotate the canvas
313
- ctx.translate(canvas.width/2, canvas.height/2);
314
- ctx.rotate(Math.PI/4);
315
 
316
- // Calculate dimensions to maintain aspect ratio
317
- const scale = Math.min(
318
- canvas.width / Math.max(sw, 1),
319
- canvas.height / Math.max(sh, 1)
320
- );
 
 
 
 
 
 
 
 
321
 
322
- const scaledWidth = sw * scale;
323
- const scaledHeight = sh * scale;
 
 
 
 
 
324
 
325
- ctx.drawImage(
326
- uploadedImage,
327
- sx, sy, sw, sh,
328
- -scaledWidth/2, -scaledHeight/2,
329
- scaledWidth, scaledHeight
330
- );
331
- ctx.setTransform(1, 0, 0, 1, 0, 0);
332
- } else {
333
- // Standard orientation - fit to canvas while maintaining aspect ratio
334
- const sourceAspect = sw / sh;
335
- const canvasAspect = canvas.width / canvas.height;
336
 
337
- let drawWidth, drawHeight, offsetX = 0, offsetY = 0;
 
 
 
 
 
 
338
 
339
- if (sourceAspect > canvasAspect) {
340
- // Source is wider than canvas relative to height
341
- drawHeight = canvas.height;
342
- drawWidth = sw * (canvas.height / sh);
343
- offsetX = (canvas.width - drawWidth) / 2;
344
- } else {
345
- // Source is taller than canvas relative to width
346
- drawWidth = canvas.width;
347
- drawHeight = sh * (canvas.width / sw);
348
- offsetY = (canvas.height - drawHeight) / 2;
349
  }
350
- // Draw the image segment with full coverage
351
- // First fill background in case of transparent areas
352
- ctx.fillStyle = 'white';
353
- ctx.fillRect(0, 0, canvas.width, canvas.height);
354
- ctx.drawImage(
355
- uploadedImage,
356
- sx, sy, sw, sh,
357
- offsetX, offsetY,
358
- drawWidth, drawHeight
359
- );
360
- }
361
- // Draw border around the image segment (red for cut lines)
362
- ctx.strokeStyle = 'rgba(255, 0, 0, 0.7)';
363
- ctx.lineWidth = 2;
364
- ctx.strokeRect(offsetX, offsetY, drawWidth, drawHeight);
365
- // Draw overlap indicators
366
- ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
367
- ctx.lineWidth = 1;
368
- ctx.setLineDash([5, 5]);
369
-
370
- // Left overlap
371
- if (col > 0) {
372
- ctx.beginPath();
373
- ctx.moveTo(0, 0);
374
- ctx.lineTo(0, canvas.height);
375
- ctx.stroke();
376
- }
377
-
378
- // Right overlap
379
- if (col < gridSize - 1) {
380
- ctx.beginPath();
381
- ctx.moveTo(canvas.width, 0);
382
- ctx.lineTo(canvas.width, canvas.height);
383
- ctx.stroke();
384
- }
385
-
386
- // Top overlap
387
- if (row > 0) {
388
- ctx.beginPath();
389
- ctx.moveTo(0, 0);
390
- ctx.lineTo(canvas.width, 0);
391
- ctx.stroke();
392
- }
393
-
394
- // Bottom overlap
395
- if (row < gridSize - 1) {
396
- ctx.beginPath();
397
- ctx.moveTo(0, canvas.height);
398
- ctx.lineTo(canvas.width, canvas.height);
399
- ctx.stroke();
400
- }
401
-
402
- ctx.restore();
403
-
404
- // Reset line dash
405
- ctx.setLineDash([]);
406
- // Draw cutting guides if enabled
407
- if (showGuides) {
408
- ctx.strokeStyle = 'rgba(255, 0, 0, 0.3)';
409
- ctx.lineWidth = 2;
410
- ctx.setLineDash([5, 5]);
411
 
412
- // Vertical guide
413
- ctx.beginPath();
414
- ctx.moveTo(canvas.width/2, 0);
415
- ctx.lineTo(canvas.width/2, canvas.height);
416
- ctx.stroke();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
- // Horizontal guide
419
- ctx.beginPath();
420
- ctx.moveTo(0, canvas.height/2);
421
- ctx.lineTo(canvas.width, canvas.height/2);
422
- ctx.stroke();
423
 
424
- ctx.setLineDash([]);
 
 
425
  }
426
- // Add page number
427
- ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
428
- ctx.fillRect(canvas.width - 30, canvas.height - 20, 25, 15);
429
- ctx.fillStyle = 'black';
430
- ctx.font = '10px Arial';
431
- ctx.textAlign = 'right';
432
- ctx.fillText(pageNumber, canvas.width - 10, canvas.height - 8);
433
- // Add canvas to page
434
- pageDiv.appendChild(canvas);
435
- posterPages.appendChild(pageDiv);
436
  }
 
 
 
437
  }
438
- // Scroll to results
439
- resultsSection.scrollIntoView({ behavior: 'smooth' });
440
- }
441
- function printAllPages() {
442
  window.print();
443
  }
444
 
 
32
  // Functions
33
  function handleImageUpload(e) {
34
  const file = e.target.files[0];
35
+ if (file && file.type.match('image.*')) {
 
 
 
 
 
 
 
 
 
 
 
36
  processImageFile(file);
37
  }
38
  }
39
+
40
+ function handleDragOver(e) {
41
  e.preventDefault();
42
  e.stopPropagation();
43
  dropZone.classList.add('drag-over');
 
59
  processImageFile(file);
60
  }
61
  }
62
+
63
  function processImageFile(file) {
64
  const reader = new FileReader();
 
 
 
 
 
65
  reader.onload = function(e) {
 
 
66
  uploadedImage = new Image();
 
 
67
  uploadedImage.onload = function() {
68
+ displayPreview(uploadedImage);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  generateBtn.disabled = false;
 
70
  };
 
 
 
 
 
 
 
71
  uploadedImage.src = e.target.result;
 
 
 
 
 
 
 
72
  };
 
73
  reader.readAsDataURL(file);
74
  }
75
+
 
76
  function displayPreview(image) {
77
+ previewContainer.innerHTML = '';
78
 
79
  const canvas = document.createElement('canvas');
 
80
  const ctx = canvas.getContext('2d');
 
 
81
  const maxWidth = 400;
82
  const maxHeight = 300;
83
  let width = image.width;
84
  let height = image.height;
85
 
86
  if (width > maxWidth) {
87
+ height = (maxWidth / width) * height;
88
  width = maxWidth;
 
89
  }
 
90
  if (height > maxHeight) {
91
+ width = (maxHeight / height) * width;
92
  height = maxHeight;
 
93
  }
94
 
95
  canvas.width = width;
96
  canvas.height = height;
 
 
97
  ctx.drawImage(image, 0, 0, width, height);
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  previewContainer.appendChild(canvas);
 
 
 
 
 
 
 
100
  }
 
 
 
 
 
101
 
102
+ function generatePosterPages() {
103
+ if (!uploadedImage) return;
104
+
105
+ posterPages.innerHTML = '';
106
+ resultsSection.classList.remove('hidden');
107
+ const pages = parseInt(pageCount.value);
108
+ const gridSize = Math.sqrt(pages);
109
+ const orient = orientation.value;
110
+ const overlapXPx = (parseInt(overlapX.value) / 25.4) * 96; // Convert mm to pixels (96dpi)
111
+ const overlapYPx = (parseInt(overlapY.value) / 25.4) * 96; // Convert mm to pixels (96dpi)
112
+ const showGuides = showGrid.checked;
113
+ const centerOverlaps = centerOverlap.checked;
114
+
115
+ // Calculate the size of each segment
116
+ const segmentWidth = uploadedImage.width / gridSize;
117
+ const segmentHeight = uploadedImage.height / gridSize;
118
+
119
+ // Create a canvas for each page
120
+ for (let row = 0; row < gridSize; row++) {
121
+ for (let col = 0; col < gridSize; col++) {
122
+ const pageNumber = row * gridSize + col + 1;
123
+ const pageDiv = document.createElement('div');
124
+ pageDiv.className = 'poster-page relative';
125
+
126
+ const canvas = document.createElement('canvas');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ // Set canvas dimensions based on orientation
129
+ if (orient === 'landscape') {
130
+ canvas.width = 842; // A4 height as width (landscape)
131
+ canvas.height = 595; // A4 width as height (landscape)
 
132
  } else {
133
+ canvas.width = 595; // A4 width in pixels at 72dpi
134
+ canvas.height = 842; // A4 height in pixels at 72dpi
135
  }
136
 
137
+ const ctx = canvas.getContext('2d');
138
+ // Calculate source rectangle with proper overlap handling
139
+ let sx, sy, sw, sh;
140
+
141
+ if (orient === 'diagonal') {
142
+ // Diagonal orientation - rotate source rectangle
143
+ const centerX = (col + 0.5) * segmentWidth;
144
+ const centerY = (row + 0.5) * segmentHeight;
145
+ const radius = Math.sqrt(segmentWidth*segmentWidth + segmentHeight*segmentHeight)/2;
146
+
147
+ sx = centerX - radius;
148
+ sy = centerY - radius;
149
+ sw = radius * 2;
150
+ sh = radius * 2;
151
  } else {
152
+ // Standard orientation
153
+ if (centerOverlaps) {
154
+ // Center overlaps - add equal overlap on all sides
155
+ sx = col * segmentWidth - overlapXPx;
156
+ sy = row * segmentHeight - overlapYPx;
157
+ sw = segmentWidth + overlapXPx * 2;
158
+ sh = segmentHeight + overlapYPx * 2;
159
+ } else {
160
+ // Traditional overlaps - add overlap to right and bottom only
161
+ sx = col * segmentWidth;
162
+ sy = row * segmentHeight;
163
+ sw = segmentWidth + (col < gridSize-1 ? overlapXPx : 0);
164
+ sh = segmentHeight + (row < gridSize-1 ? overlapYPx : 0);
165
+ }
166
  }
167
+ // Adjust source rectangle to stay within image bounds
168
+ let dx = 0, dy = 0;
 
 
169
 
170
+ if (sx < 0) {
171
+ dx = -sx;
172
+ sw += sx;
173
+ sx = 0;
174
+ }
175
+ if (sy < 0) {
176
+ dy = -sy;
177
+ sh += sy;
178
+ sy = 0;
179
  }
180
 
181
+ // Adjust width/height if extending beyond image
182
+ sw = Math.min(sw, uploadedImage.width - sx);
183
+ sh = Math.min(sh, uploadedImage.height - sy);
184
+ // Draw the image segment with border
185
+ ctx.save();
186
+ if (orient === 'diagonal') {
187
+ // For diagonal orientation, rotate the canvas
188
+ ctx.translate(canvas.width/2, canvas.height/2);
189
+ ctx.rotate(Math.PI/4);
190
+ ctx.drawImage(
191
+ uploadedImage,
192
+ sx, sy, sw, sh,
193
+ -canvas.width/2, -canvas.height/2, canvas.width, canvas.height
194
+ );
195
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
196
  } else {
197
+ // Standard orientation
198
+ // Calculate destination rectangle with proper aspect ratio
199
+ const destWidth = canvas.width;
200
+ const destHeight = canvas.height;
201
+
202
+ // Adjust destination position based on any out-of-bounds
203
+ const destX = -dx * (destWidth / sw);
204
+ const destY = -dy * (destHeight / sh);
205
+
206
+ ctx.drawImage(
207
+ uploadedImage,
208
+ sx, sy, sw, sh,
209
+ destX, destY,
210
+ destWidth * (sw / (sw + dx)),
211
+ destHeight * (sh / (sh + dy))
212
+ );
213
  }
214
+ // Draw border around the image segment
215
+ ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
216
+ ctx.lineWidth = 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
+ if (orient === 'portrait' || orient === 'landscape') {
219
+ const borderX = dx > 0 ? dx * (canvas.width / sw) : 0;
220
+ const borderY = dy > 0 ? dy * (canvas.height / sh) : 0;
221
+ const borderWidth = canvas.width * (sw / (sw + dx));
222
+ const borderHeight = canvas.height * (sh / (sh + dy));
223
+ ctx.strokeRect(borderX, borderY, borderWidth, borderHeight);
224
+ } else {
225
+ ctx.strokeRect(0, 0, canvas.width, canvas.height);
226
+ }
227
+ // Draw overlap indicators
228
+ ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
229
+ ctx.lineWidth = 1;
230
+ ctx.setLineDash([5, 5]);
231
 
232
+ // Left overlap
233
+ if (col > 0) {
234
+ ctx.beginPath();
235
+ ctx.moveTo(0, 0);
236
+ ctx.lineTo(0, canvas.height);
237
+ ctx.stroke();
238
+ }
239
 
240
+ // Right overlap
241
+ if (col < gridSize - 1) {
242
+ ctx.beginPath();
243
+ ctx.moveTo(canvas.width, 0);
244
+ ctx.lineTo(canvas.width, canvas.height);
245
+ ctx.stroke();
246
+ }
 
 
 
 
247
 
248
+ // Top overlap
249
+ if (row > 0) {
250
+ ctx.beginPath();
251
+ ctx.moveTo(0, 0);
252
+ ctx.lineTo(canvas.width, 0);
253
+ ctx.stroke();
254
+ }
255
 
256
+ // Bottom overlap
257
+ if (row < gridSize - 1) {
258
+ ctx.beginPath();
259
+ ctx.moveTo(0, canvas.height);
260
+ ctx.lineTo(canvas.width, canvas.height);
261
+ ctx.stroke();
 
 
 
 
262
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
+ ctx.restore();
265
+ // Add cutting guides if enabled
266
+ if (showGuides) {
267
+ const guideSize = 20;
268
+
269
+ // Top guide
270
+ if (row > 0) {
271
+ const topGuide = document.createElement('div');
272
+ topGuide.className = 'cutting-guide';
273
+ topGuide.style.top = '0';
274
+ topGuide.style.left = '0';
275
+ topGuide.style.width = '100%';
276
+ topGuide.style.height = `${guideSize}px`;
277
+ pageDiv.appendChild(topGuide);
278
+ }
279
+
280
+ // Left guide
281
+ if (col > 0) {
282
+ const leftGuide = document.createElement('div');
283
+ leftGuide.className = 'cutting-guide';
284
+ leftGuide.style.top = '0';
285
+ leftGuide.style.left = '0';
286
+ leftGuide.style.width = `${guideSize}px`;
287
+ leftGuide.style.height = '100%';
288
+ pageDiv.appendChild(leftGuide);
289
+ }
290
+ }
291
 
292
+ // Add page number
293
+ const pageNumberDiv = document.createElement('div');
294
+ pageNumberDiv.className = 'page-number';
295
+ pageNumberDiv.textContent = pageNumber;
296
+ pageDiv.appendChild(pageNumberDiv);
297
 
298
+ // Add canvas to page
299
+ pageDiv.appendChild(canvas);
300
+ posterPages.appendChild(pageDiv);
301
  }
 
 
 
 
 
 
 
 
 
 
302
  }
303
+
304
+ // Scroll to results
305
+ resultsSection.scrollIntoView({ behavior: 'smooth' });
306
  }
307
+
308
+ function printAllPages() {
 
 
309
  window.print();
310
  }
311
 
style.css CHANGED
@@ -3,36 +3,7 @@
3
  border-color: #3b82f6;
4
  background-color: #f0f7ff;
5
  }
6
- #previewContainer {
7
- min-height: 300px;
8
- display: flex;
9
- align-items: center;
10
- justify-content: center;
11
- }
12
-
13
- #previewContainer canvas {
14
- max-width: 100%;
15
- height: auto;
16
- display: block;
17
- margin: 0 auto;
18
- }
19
 
20
- @keyframes spin {
21
- to { transform: rotate(360deg); }
22
- }
23
-
24
- .animate-spin {
25
- animation: spin 1s linear infinite;
26
- }
27
- .preview-grid-label {
28
- position: absolute;
29
- background: rgba(255,255,255,0.7);
30
- padding: 2px 5px;
31
- border-radius: 3px;
32
- font-size: 11px;
33
- font-weight: bold;
34
- color: #333;
35
- }
36
  .poster-page {
37
  position: relative;
38
  overflow: hidden;
@@ -49,11 +20,14 @@
49
  height: 100%;
50
  object-fit: cover;
51
  }
 
 
 
 
 
 
52
  .poster-page canvas {
53
- background: white;
54
- border: none;
55
- width: 100%;
56
- height: 100%;
57
  }
58
  .page-number {
59
  position: absolute;
 
3
  border-color: #3b82f6;
4
  background-color: #f0f7ff;
5
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  .poster-page {
8
  position: relative;
9
  overflow: hidden;
 
20
  height: 100%;
21
  object-fit: cover;
22
  }
23
+ .cutting-guide {
24
+ position: absolute;
25
+ background-color: rgba(255, 0, 0, 0.3);
26
+ z-index: 10;
27
+ }
28
+
29
  .poster-page canvas {
30
+ border: 1px dashed rgba(0, 0, 255, 0.5);
 
 
 
31
  }
32
  .page-number {
33
  position: absolute;