beta3 commited on
Commit
274e249
·
verified ·
1 Parent(s): 2919584

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +927 -19
index.html CHANGED
@@ -1,19 +1,927 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PDF Crop Tool</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>
9
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=Syne:wght@600;700&display=swap" rel="stylesheet">
10
+ <style>
11
+ :root {
12
+ --color-bg: #fafafa;
13
+ --color-surface: #ffffff;
14
+ --color-text: #0f172a;
15
+ --color-text-muted: #64748b;
16
+ --color-primary: #1e293b;
17
+ --color-accent: #334155;
18
+ --color-border: #e2e8f0;
19
+ --color-success: #059669;
20
+ --color-error: #dc2626;
21
+ }
22
+
23
+ * {
24
+ margin: 0;
25
+ padding: 0;
26
+ box-sizing: border-box;
27
+ }
28
+
29
+ body {
30
+ font-family: 'DM Sans', sans-serif;
31
+ background: var(--color-bg);
32
+ color: var(--color-text);
33
+ line-height: 1.7;
34
+ overflow-x: hidden;
35
+ position: relative;
36
+ min-height: 100vh;
37
+ }
38
+
39
+ .geometric-bg {
40
+ position: fixed;
41
+ top: 0;
42
+ left: 0;
43
+ width: 100%;
44
+ height: 100%;
45
+ z-index: 0;
46
+ overflow: hidden;
47
+ pointer-events: none;
48
+ }
49
+
50
+ .shape {
51
+ position: absolute;
52
+ opacity: 0.08;
53
+ }
54
+
55
+ .shape-1 {
56
+ width: 500px;
57
+ height: 500px;
58
+ border: 3px solid var(--color-primary);
59
+ border-radius: 50%;
60
+ top: -150px;
61
+ right: -150px;
62
+ animation: float 20s ease-in-out infinite;
63
+ }
64
+
65
+ .shape-2 {
66
+ width: 400px;
67
+ height: 400px;
68
+ border: 3px solid var(--color-accent);
69
+ transform: rotate(45deg);
70
+ bottom: -100px;
71
+ left: -100px;
72
+ animation: float 25s ease-in-out infinite reverse;
73
+ }
74
+
75
+ .shape-3 {
76
+ width: 300px;
77
+ height: 300px;
78
+ border: 2px solid var(--color-primary);
79
+ border-radius: 30%;
80
+ top: 40%;
81
+ left: 5%;
82
+ animation: float 30s ease-in-out infinite;
83
+ }
84
+
85
+ .shape-4 {
86
+ width: 250px;
87
+ height: 250px;
88
+ background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
89
+ opacity: 0.05;
90
+ border-radius: 50%;
91
+ top: 15%;
92
+ right: 15%;
93
+ animation: pulse 15s ease-in-out infinite;
94
+ }
95
+
96
+ .shape-5 {
97
+ width: 350px;
98
+ height: 350px;
99
+ border: 2px solid var(--color-accent);
100
+ border-radius: 40%;
101
+ top: 60%;
102
+ right: 30%;
103
+ animation: float 35s ease-in-out infinite;
104
+ }
105
+
106
+ .shape-6 {
107
+ width: 200px;
108
+ height: 200px;
109
+ border: 3px solid var(--color-primary);
110
+ transform: rotate(30deg);
111
+ top: 25%;
112
+ left: 40%;
113
+ animation: float 28s ease-in-out infinite reverse;
114
+ }
115
+
116
+ .shape-7 {
117
+ width: 180px;
118
+ height: 180px;
119
+ background: linear-gradient(45deg, var(--color-accent), var(--color-primary));
120
+ opacity: 0.04;
121
+ border-radius: 50%;
122
+ bottom: 20%;
123
+ right: 40%;
124
+ animation: pulse 20s ease-in-out infinite;
125
+ }
126
+
127
+ @keyframes float {
128
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
129
+ 25% { transform: translate(30px, -30px) rotate(8deg); }
130
+ 50% { transform: translate(-30px, 30px) rotate(-8deg); }
131
+ 75% { transform: translate(30px, 30px) rotate(8deg); }
132
+ }
133
+
134
+ @keyframes pulse {
135
+ 0%, 100% { transform: scale(1); opacity: 0.04; }
136
+ 50% { transform: scale(1.15); opacity: 0.08; }
137
+ }
138
+
139
+ @keyframes fadeInUp {
140
+ from {
141
+ opacity: 0;
142
+ transform: translateY(30px);
143
+ }
144
+ to {
145
+ opacity: 1;
146
+ transform: translateY(0);
147
+ }
148
+ }
149
+
150
+ .container {
151
+ max-width: 1100px;
152
+ margin: 0 auto;
153
+ padding: 60px 24px;
154
+ position: relative;
155
+ z-index: 1;
156
+ }
157
+
158
+ header {
159
+ text-align: center;
160
+ margin-bottom: 80px;
161
+ animation: fadeInUp 0.8s ease-out;
162
+ }
163
+
164
+ h1 {
165
+ font-family: 'Syne', sans-serif;
166
+ font-size: 52px;
167
+ font-weight: 700;
168
+ margin-bottom: 16px;
169
+ letter-spacing: -0.02em;
170
+ line-height: 1.1;
171
+ }
172
+
173
+ .subtitle {
174
+ color: var(--color-text-muted);
175
+ font-size: 18px;
176
+ font-weight: 400;
177
+ max-width: 500px;
178
+ margin: 0 auto;
179
+ }
180
+
181
+ .upload-section {
182
+ background: var(--color-surface);
183
+ padding: 80px 48px;
184
+ border-radius: 24px;
185
+ border: 1px solid var(--color-border);
186
+ margin-bottom: 40px;
187
+ text-align: center;
188
+ position: relative;
189
+ overflow: hidden;
190
+ animation: fadeInUp 0.8s ease-out 0.2s both;
191
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
192
+ }
193
+
194
+ .upload-section::before {
195
+ content: '';
196
+ position: absolute;
197
+ top: 0;
198
+ left: 0;
199
+ right: 0;
200
+ height: 1px;
201
+ background: linear-gradient(90deg, transparent, var(--color-border), transparent);
202
+ }
203
+
204
+ .upload-section:hover {
205
+ transform: translateY(-4px);
206
+ box-shadow: 0 20px 60px rgba(15, 23, 42, 0.08);
207
+ }
208
+
209
+ .upload-section h2 {
210
+ font-family: 'Syne', sans-serif;
211
+ font-size: 24px;
212
+ font-weight: 600;
213
+ margin-bottom: 32px;
214
+ color: var(--color-text);
215
+ }
216
+
217
+ .file-input-wrapper {
218
+ position: relative;
219
+ display: inline-block;
220
+ }
221
+
222
+ input[type="file"] {
223
+ display: none;
224
+ }
225
+
226
+ .upload-btn {
227
+ background: var(--color-primary);
228
+ color: white;
229
+ padding: 16px 40px;
230
+ border: none;
231
+ border-radius: 12px;
232
+ font-size: 15px;
233
+ font-weight: 500;
234
+ cursor: pointer;
235
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
236
+ position: relative;
237
+ overflow: hidden;
238
+ }
239
+
240
+ .upload-btn::before {
241
+ content: '';
242
+ position: absolute;
243
+ top: 50%;
244
+ left: 50%;
245
+ width: 0;
246
+ height: 0;
247
+ border-radius: 50%;
248
+ background: rgba(255, 255, 255, 0.1);
249
+ transform: translate(-50%, -50%);
250
+ transition: width 0.6s, height 0.6s;
251
+ }
252
+
253
+ .upload-btn:hover::before {
254
+ width: 300px;
255
+ height: 300px;
256
+ }
257
+
258
+ .upload-btn:hover {
259
+ transform: translateY(-2px);
260
+ box-shadow: 0 12px 40px rgba(30, 41, 59, 0.3);
261
+ }
262
+
263
+ .upload-hint {
264
+ margin-top: 24px;
265
+ color: var(--color-text-muted);
266
+ font-size: 14px;
267
+ }
268
+
269
+ .canvas-section {
270
+ background: var(--color-surface);
271
+ padding: 48px;
272
+ border-radius: 24px;
273
+ border: 1px solid var(--color-border);
274
+ margin-bottom: 40px;
275
+ display: none;
276
+ animation: fadeInUp 0.8s ease-out;
277
+ }
278
+
279
+ .canvas-wrapper {
280
+ position: relative;
281
+ display: inline-block;
282
+ margin: 32px auto;
283
+ border-radius: 12px;
284
+ overflow: hidden;
285
+ box-shadow: 0 8px 32px rgba(15, 23, 42, 0.1);
286
+ }
287
+
288
+ canvas {
289
+ display: block;
290
+ cursor: crosshair;
291
+ }
292
+
293
+ .selection-box {
294
+ position: absolute;
295
+ border: 2px solid var(--color-primary);
296
+ background: rgba(30, 41, 59, 0.08);
297
+ pointer-events: none;
298
+ backdrop-filter: blur(2px);
299
+ }
300
+
301
+ .instructions {
302
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
303
+ padding: 24px;
304
+ border-radius: 16px;
305
+ margin-bottom: 32px;
306
+ font-size: 15px;
307
+ color: var(--color-text-muted);
308
+ border-left: 3px solid var(--color-primary);
309
+ line-height: 1.6;
310
+ }
311
+
312
+ .instructions strong {
313
+ color: var(--color-text);
314
+ font-weight: 600;
315
+ }
316
+
317
+ .button-group {
318
+ display: flex;
319
+ gap: 16px;
320
+ justify-content: center;
321
+ margin-top: 32px;
322
+ flex-wrap: wrap;
323
+ }
324
+
325
+ .btn {
326
+ padding: 14px 32px;
327
+ border: none;
328
+ border-radius: 12px;
329
+ font-size: 15px;
330
+ font-weight: 500;
331
+ cursor: pointer;
332
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
333
+ font-family: 'DM Sans', sans-serif;
334
+ }
335
+
336
+ .btn-primary {
337
+ background: var(--color-success);
338
+ color: white;
339
+ box-shadow: 0 4px 16px rgba(5, 150, 105, 0.2);
340
+ }
341
+
342
+ .btn-primary:hover:not(:disabled) {
343
+ background: #047857;
344
+ transform: translateY(-2px);
345
+ box-shadow: 0 8px 24px rgba(5, 150, 105, 0.3);
346
+ }
347
+
348
+ .btn-primary:disabled {
349
+ background: #cbd5e1;
350
+ cursor: not-allowed;
351
+ box-shadow: none;
352
+ }
353
+
354
+ .btn-secondary {
355
+ background: transparent;
356
+ color: var(--color-text);
357
+ border: 2px solid var(--color-border);
358
+ }
359
+
360
+ .btn-secondary:hover {
361
+ background: var(--color-bg);
362
+ border-color: var(--color-accent);
363
+ transform: translateY(-2px);
364
+ }
365
+
366
+ .btn-reset {
367
+ background: var(--color-error);
368
+ color: white;
369
+ box-shadow: 0 4px 16px rgba(220, 38, 38, 0.2);
370
+ }
371
+
372
+ .btn-reset:hover {
373
+ background: #b91c1c;
374
+ transform: translateY(-2px);
375
+ box-shadow: 0 8px 24px rgba(220, 38, 38, 0.3);
376
+ }
377
+
378
+ .status {
379
+ text-align: center;
380
+ padding: 16px 24px;
381
+ border-radius: 12px;
382
+ margin-top: 32px;
383
+ font-size: 15px;
384
+ display: none;
385
+ animation: fadeInUp 0.4s ease-out;
386
+ }
387
+
388
+ .status.success {
389
+ background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
390
+ color: var(--color-success);
391
+ border: 1px solid #6ee7b7;
392
+ display: block;
393
+ }
394
+
395
+ .status.processing {
396
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
397
+ color: #92400e;
398
+ border: 1px solid #fcd34d;
399
+ display: block;
400
+ }
401
+
402
+ .status.error {
403
+ background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
404
+ color: var(--color-error);
405
+ border: 1px solid #fca5a5;
406
+ display: block;
407
+ }
408
+
409
+ .loader {
410
+ border: 3px solid rgba(30, 41, 59, 0.1);
411
+ border-top: 3px solid var(--color-primary);
412
+ border-radius: 50%;
413
+ width: 20px;
414
+ height: 20px;
415
+ animation: spin 0.8s linear infinite;
416
+ display: inline-block;
417
+ margin-right: 12px;
418
+ vertical-align: middle;
419
+ }
420
+
421
+ @keyframes spin {
422
+ 0% { transform: rotate(0deg); }
423
+ 100% { transform: rotate(360deg); }
424
+ }
425
+
426
+ .modal-overlay {
427
+ position: fixed;
428
+ top: 0;
429
+ left: 0;
430
+ right: 0;
431
+ bottom: 0;
432
+ background: rgba(15, 23, 42, 0.6);
433
+ backdrop-filter: blur(8px);
434
+ z-index: 1000;
435
+ display: none;
436
+ align-items: center;
437
+ justify-content: center;
438
+ animation: fadeIn 0.3s ease-out;
439
+ }
440
+
441
+ .modal-overlay.active {
442
+ display: flex;
443
+ }
444
+
445
+ @keyframes fadeIn {
446
+ from { opacity: 0; }
447
+ to { opacity: 1; }
448
+ }
449
+
450
+ .modal-content {
451
+ background: var(--color-surface);
452
+ padding: 48px;
453
+ border-radius: 24px;
454
+ text-align: center;
455
+ max-width: 450px;
456
+ width: 90%;
457
+ box-shadow: 0 24px 80px rgba(15, 23, 42, 0.2);
458
+ animation: scaleIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
459
+ position: relative;
460
+ }
461
+
462
+ @keyframes scaleIn {
463
+ from {
464
+ opacity: 0;
465
+ transform: scale(0.9);
466
+ }
467
+ to {
468
+ opacity: 1;
469
+ transform: scale(1);
470
+ }
471
+ }
472
+
473
+ .modal-icon {
474
+ width: 80px;
475
+ height: 80px;
476
+ margin: 0 auto 24px;
477
+ position: relative;
478
+ }
479
+
480
+ .processing-spinner {
481
+ width: 80px;
482
+ height: 80px;
483
+ border: 3px solid var(--color-border);
484
+ border-top: 3px solid var(--color-primary);
485
+ border-radius: 50%;
486
+ animation: spin 1s linear infinite;
487
+ }
488
+
489
+ .success-icon {
490
+ width: 80px;
491
+ height: 80px;
492
+ border-radius: 50%;
493
+ background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
494
+ display: flex;
495
+ align-items: center;
496
+ justify-content: center;
497
+ animation: successPop 0.6s cubic-bezier(0.4, 0, 0.2, 1);
498
+ }
499
+
500
+ .success-icon::after {
501
+ content: '✓';
502
+ font-size: 48px;
503
+ color: var(--color-success);
504
+ font-weight: 700;
505
+ }
506
+
507
+ @keyframes successPop {
508
+ 0% {
509
+ transform: scale(0);
510
+ opacity: 0;
511
+ }
512
+ 50% {
513
+ transform: scale(1.1);
514
+ }
515
+ 100% {
516
+ transform: scale(1);
517
+ opacity: 1;
518
+ }
519
+ }
520
+
521
+ .modal-title {
522
+ font-family: 'Syne', sans-serif;
523
+ font-size: 28px;
524
+ font-weight: 700;
525
+ margin-bottom: 12px;
526
+ color: var(--color-text);
527
+ }
528
+
529
+ .modal-text {
530
+ color: var(--color-text-muted);
531
+ font-size: 15px;
532
+ margin-bottom: 32px;
533
+ line-height: 1.6;
534
+ }
535
+
536
+ .progress-bar {
537
+ width: 100%;
538
+ height: 4px;
539
+ background: var(--color-border);
540
+ border-radius: 2px;
541
+ overflow: hidden;
542
+ margin-top: 24px;
543
+ }
544
+
545
+ .progress-fill {
546
+ height: 100%;
547
+ background: var(--color-primary);
548
+ border-radius: 2px;
549
+ animation: progressFlow 2s ease-in-out infinite;
550
+ }
551
+
552
+ @keyframes progressFlow {
553
+ 0% {
554
+ width: 0%;
555
+ opacity: 1;
556
+ }
557
+ 50% {
558
+ width: 100%;
559
+ opacity: 1;
560
+ }
561
+ 100% {
562
+ width: 100%;
563
+ opacity: 0;
564
+ }
565
+ }
566
+
567
+ .modal-btn {
568
+ background: var(--color-primary);
569
+ color: white;
570
+ padding: 16px 48px;
571
+ border: none;
572
+ border-radius: 12px;
573
+ font-size: 15px;
574
+ font-weight: 500;
575
+ cursor: pointer;
576
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
577
+ font-family: 'DM Sans', sans-serif;
578
+ width: 100%;
579
+ margin-bottom: 12px;
580
+ }
581
+
582
+ .modal-btn:hover {
583
+ transform: translateY(-2px);
584
+ box-shadow: 0 12px 40px rgba(30, 41, 59, 0.3);
585
+ }
586
+
587
+ .modal-btn-secondary {
588
+ background: transparent;
589
+ color: var(--color-text-muted);
590
+ border: 2px solid var(--color-border);
591
+ margin-top: 8px;
592
+ }
593
+
594
+ .modal-btn-secondary:hover {
595
+ background: var(--color-bg);
596
+ border-color: var(--color-accent);
597
+ }
598
+
599
+ @media (max-width: 768px) {
600
+ h1 {
601
+ font-size: 36px;
602
+ }
603
+
604
+ .container {
605
+ padding: 40px 20px;
606
+ }
607
+
608
+ .upload-section {
609
+ padding: 60px 32px;
610
+ }
611
+
612
+ .canvas-section {
613
+ padding: 32px 24px;
614
+ }
615
+
616
+ .button-group {
617
+ flex-direction: column;
618
+ }
619
+ }
620
+ </style>
621
+ </head>
622
+ <body>
623
+ <div class="geometric-bg">
624
+ <div class="shape shape-1"></div>
625
+ <div class="shape shape-2"></div>
626
+ <div class="shape shape-3"></div>
627
+ <div class="shape shape-4"></div>
628
+ <div class="shape shape-5"></div>
629
+ <div class="shape shape-6"></div>
630
+ <div class="shape shape-7"></div>
631
+ </div>
632
+
633
+ <div class="container">
634
+ <header>
635
+ <h1>PDF Crop Tool</h1>
636
+ <p class="subtitle">Crop the same area across all PDF pages</p>
637
+ </header>
638
+
639
+ <div class="upload-section" id="uploadSection">
640
+ <h2>Upload Document</h2>
641
+ <div class="file-input-wrapper">
642
+ <input type="file" id="pdfInput" accept=".pdf">
643
+ <button class="upload-btn" onclick="document.getElementById('pdfInput').click()">
644
+ Select File
645
+ </button>
646
+ </div>
647
+ <p class="upload-hint">Supported formats: PDF</p>
648
+ </div>
649
+
650
+ <div class="canvas-section" id="canvasSection">
651
+ <div class="instructions">
652
+ <strong>Instructions:</strong> Click and drag on the first page to select the area you want to crop. This area will be applied to all pages of the document.
653
+ </div>
654
+
655
+ <div style="text-align: center;">
656
+ <div class="canvas-wrapper" id="canvasWrapper">
657
+ <canvas id="pdfCanvas"></canvas>
658
+ <div class="selection-box" id="selectionBox"></div>
659
+ </div>
660
+ </div>
661
+
662
+ <div class="button-group">
663
+ <button class="btn btn-secondary" id="resetBtn">Clear Selection</button>
664
+ <button class="btn btn-primary" id="cropBtn" disabled>Process PDF</button>
665
+ <button class="btn btn-reset" id="startOverBtn">Start Over</button>
666
+ </div>
667
+
668
+ <div class="status" id="status"></div>
669
+ </div>
670
+ </div>
671
+
672
+ <div class="modal-overlay" id="modalOverlay">
673
+ <div class="modal-content">
674
+ <div class="modal-icon" id="modalIcon">
675
+ <div class="processing-spinner"></div>
676
+ </div>
677
+ <h2 class="modal-title" id="modalTitle">Processing PDF</h2>
678
+ <p class="modal-text" id="modalText">Please wait while we process your document...</p>
679
+ <div class="progress-bar" id="progressBar">
680
+ <div class="progress-fill"></div>
681
+ </div>
682
+ <div id="modalButtons" style="display: none;">
683
+ <button class="modal-btn" id="downloadBtn">Download PDF</button>
684
+ <button class="modal-btn modal-btn-secondary" id="newDocBtn">Process New Document</button>
685
+ </div>
686
+ </div>
687
+ </div>
688
+
689
+ <script>
690
+ pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
691
+
692
+ let pdfDoc = null;
693
+ let pdfBytes = null;
694
+ let pdfArrayBuffer = null;
695
+ let processedPdfBlob = null;
696
+ let canvas = document.getElementById('pdfCanvas');
697
+ let ctx = canvas.getContext('2d');
698
+ let isSelecting = false;
699
+ let startX, startY, endX, endY;
700
+ let selection = null;
701
+
702
+ document.getElementById('pdfInput').addEventListener('change', handleFileSelect);
703
+ canvas.addEventListener('mousedown', handleMouseDown);
704
+ canvas.addEventListener('mousemove', handleMouseMove);
705
+ canvas.addEventListener('mouseup', handleMouseUp);
706
+ document.getElementById('resetBtn').addEventListener('click', resetSelection);
707
+ document.getElementById('cropBtn').addEventListener('click', processPDF);
708
+ document.getElementById('downloadBtn').addEventListener('click', downloadPDF);
709
+ document.getElementById('startOverBtn').addEventListener('click', startOver);
710
+ document.getElementById('newDocBtn').addEventListener('click', startOver);
711
+
712
+ async function handleFileSelect(e) {
713
+ const file = e.target.files[0];
714
+ if (!file) return;
715
+
716
+ try {
717
+ const originalBuffer = await file.arrayBuffer();
718
+
719
+ pdfBytes = new Uint8Array(originalBuffer.slice(0));
720
+
721
+ pdfArrayBuffer = originalBuffer.slice(0);
722
+
723
+ const loadingTask = pdfjsLib.getDocument({data: pdfBytes});
724
+ pdfDoc = await loadingTask.promise;
725
+
726
+ await renderFirstPage();
727
+
728
+ document.getElementById('uploadSection').style.display = 'none';
729
+ document.getElementById('canvasSection').style.display = 'block';
730
+ showStatus('PDF loaded successfully. Select the area to crop.', 'success');
731
+ } catch (error) {
732
+ showStatus('Error loading PDF: ' + error.message, 'error');
733
+ }
734
+ }
735
+
736
+ async function renderFirstPage() {
737
+ const page = await pdfDoc.getPage(1);
738
+ const viewport = page.getViewport({ scale: 1.5 });
739
+
740
+ canvas.width = viewport.width;
741
+ canvas.height = viewport.height;
742
+
743
+ await page.render({
744
+ canvasContext: ctx,
745
+ viewport: viewport
746
+ }).promise;
747
+ }
748
+
749
+ function handleMouseDown(e) {
750
+ const rect = canvas.getBoundingClientRect();
751
+ startX = e.clientX - rect.left;
752
+ startY = e.clientY - rect.top;
753
+ isSelecting = true;
754
+ }
755
+
756
+ function handleMouseMove(e) {
757
+ if (!isSelecting) return;
758
+
759
+ const rect = canvas.getBoundingClientRect();
760
+ endX = e.clientX - rect.left;
761
+ endY = e.clientY - rect.top;
762
+
763
+ updateSelectionBox();
764
+ }
765
+
766
+ function handleMouseUp(e) {
767
+ if (!isSelecting) return;
768
+
769
+ isSelecting = false;
770
+ const rect = canvas.getBoundingClientRect();
771
+ endX = e.clientX - rect.left;
772
+ endY = e.clientY - rect.top;
773
+
774
+ const x = Math.min(startX, endX);
775
+ const y = Math.min(startY, endY);
776
+ const width = Math.abs(endX - startX);
777
+ const height = Math.abs(endY - startY);
778
+
779
+ if (width > 5 && height > 5) {
780
+ selection = { x, y, width, height };
781
+ updateSelectionBox();
782
+ document.getElementById('cropBtn').disabled = false;
783
+ }
784
+ }
785
+
786
+ function updateSelectionBox() {
787
+ const box = document.getElementById('selectionBox');
788
+ const x = Math.min(startX, endX);
789
+ const y = Math.min(startY, endY);
790
+ const width = Math.abs(endX - startX);
791
+ const height = Math.abs(endY - startY);
792
+
793
+ box.style.left = x + 'px';
794
+ box.style.top = y + 'px';
795
+ box.style.width = width + 'px';
796
+ box.style.height = height + 'px';
797
+ box.style.display = 'block';
798
+ }
799
+
800
+ function resetSelection() {
801
+ selection = null;
802
+ document.getElementById('selectionBox').style.display = 'none';
803
+ document.getElementById('cropBtn').disabled = true;
804
+ }
805
+
806
+ async function processPDF() {
807
+ if (!selection || !pdfArrayBuffer) return;
808
+
809
+ try {
810
+ showModal();
811
+ document.getElementById('cropBtn').disabled = true;
812
+
813
+ const page = await pdfDoc.getPage(1);
814
+ const viewport = page.getViewport({ scale: 1.5 });
815
+
816
+ const scaleX = page.view[2] / viewport.width;
817
+ const scaleY = page.view[3] / viewport.height;
818
+
819
+ const cropBox = {
820
+ x: selection.x * scaleX,
821
+ y: (viewport.height - selection.y - selection.height) * scaleY,
822
+ width: selection.width * scaleX,
823
+ height: selection.height * scaleY
824
+ };
825
+
826
+ const pdfLibDoc = await PDFLib.PDFDocument.load(pdfArrayBuffer);
827
+ const newPdf = await PDFLib.PDFDocument.create();
828
+
829
+ const numPages = pdfLibDoc.getPageCount();
830
+
831
+ for (let i = 0; i < numPages; i++) {
832
+ const [croppedPage] = await newPdf.copyPages(pdfLibDoc, [i]);
833
+
834
+ croppedPage.setCropBox(
835
+ cropBox.x,
836
+ cropBox.y,
837
+ cropBox.width,
838
+ cropBox.height
839
+ );
840
+
841
+ croppedPage.setMediaBox(
842
+ cropBox.x,
843
+ cropBox.y,
844
+ cropBox.width,
845
+ cropBox.height
846
+ );
847
+
848
+ newPdf.addPage(croppedPage);
849
+ }
850
+
851
+ const pdfBytesOutput = await newPdf.save();
852
+ processedPdfBlob = new Blob([pdfBytesOutput], { type: 'application/pdf' });
853
+
854
+ showSuccess();
855
+ document.getElementById('cropBtn').disabled = false;
856
+ } catch (error) {
857
+ closeModal();
858
+ showStatus('Error processing PDF: ' + error.message, 'error');
859
+ document.getElementById('cropBtn').disabled = false;
860
+ }
861
+ }
862
+
863
+ function showModal() {
864
+ document.getElementById('modalOverlay').classList.add('active');
865
+ document.getElementById('modalIcon').innerHTML = '<div class="processing-spinner"></div>';
866
+ document.getElementById('modalTitle').textContent = 'Processing PDF';
867
+ document.getElementById('modalText').textContent = 'Please wait while we process your document...';
868
+ document.getElementById('progressBar').style.display = 'block';
869
+ document.getElementById('modalButtons').style.display = 'none';
870
+ }
871
+
872
+ function showSuccess() {
873
+ document.getElementById('modalIcon').innerHTML = '<div class="success-icon"></div>';
874
+ document.getElementById('modalTitle').textContent = 'PDF Processed';
875
+ document.getElementById('modalText').textContent = 'Your document has been processed successfully and is ready to download.';
876
+ document.getElementById('progressBar').style.display = 'none';
877
+ document.getElementById('modalButtons').style.display = 'block';
878
+ }
879
+
880
+ function downloadPDF() {
881
+ if (!processedPdfBlob) return;
882
+
883
+ const url = URL.createObjectURL(processedPdfBlob);
884
+ const a = document.createElement('a');
885
+ a.href = url;
886
+ a.download = 'cropped_document.pdf';
887
+ a.click();
888
+ URL.revokeObjectURL(url);
889
+ }
890
+
891
+ function closeModal() {
892
+ document.getElementById('modalOverlay').classList.remove('active');
893
+ }
894
+
895
+ function startOver() {
896
+ pdfDoc = null;
897
+ pdfBytes = null;
898
+ pdfArrayBuffer = null;
899
+ processedPdfBlob = null;
900
+ selection = null;
901
+
902
+ document.getElementById('pdfInput').value = '';
903
+ document.getElementById('selectionBox').style.display = 'none';
904
+ document.getElementById('cropBtn').disabled = true;
905
+ document.getElementById('status').style.display = 'none';
906
+
907
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
908
+
909
+ document.getElementById('canvasSection').style.display = 'none';
910
+ document.getElementById('uploadSection').style.display = 'block';
911
+
912
+ closeModal();
913
+ }
914
+
915
+ function showStatus(message, type) {
916
+ const status = document.getElementById('status');
917
+ status.className = 'status ' + type;
918
+
919
+ if (type === 'processing') {
920
+ status.innerHTML = '<span class="loader"></span>' + message;
921
+ } else {
922
+ status.textContent = message;
923
+ }
924
+ }
925
+ </script>
926
+ </body>
927
+ </html>