joermd commited on
Commit
fdbe166
·
verified ·
1 Parent(s): d0d3666

Update ocr.html

Browse files
Files changed (1) hide show
  1. ocr.html +504 -416
ocr.html CHANGED
@@ -1,514 +1,602 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
- <title>UFastPro OCR Technology</title>
5
  <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1">
7
- <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
8
- <!-- PDF.js library -->
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
10
  <style>
11
- :root {
12
- --primary: #4361ee;
13
- --primary-hover: #3a56d4;
14
- --secondary: #4cc9f0;
15
- --secondary-hover: #3db8dd;
16
- }
17
-
18
  body {
 
 
 
19
  background-color: #f8f9fa;
20
  }
21
-
22
- .container {
23
- max-width: 900px;
24
- }
25
-
26
- header {
27
- margin-bottom: 2rem;
28
- padding: 1rem 0;
29
- background: linear-gradient(135deg, var(--primary), var(--secondary));
30
  color: white;
31
- border-radius: 0 0 10px 10px;
32
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
33
- }
34
-
35
- header h1 {
36
- margin: 0;
37
- font-weight: bold;
38
  }
39
-
40
- header p {
41
- margin: 0.5rem 0 0 0;
42
- opacity: 0.9;
43
  }
44
-
45
- article {
46
- margin: 2rem 0;
47
- padding: 2rem;
48
  background-color: white;
49
  border-radius: 10px;
50
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 
 
51
  }
52
-
53
- .progress-container {
54
- width: 100%;
55
- margin: 20px 0;
 
56
  }
57
-
58
- .progress-bar {
59
- height: 20px;
60
- background: linear-gradient(to right, var(--primary), var(--secondary));
61
- width: 0%;
62
  border-radius: 5px;
63
- transition: width 0.3s;
64
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
65
- }
66
-
67
- .page-result {
68
- margin-bottom: 20px;
69
- border: 1px solid #e9ecef;
70
- padding: 20px;
71
- border-radius: 8px;
72
- background-color: white;
73
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
74
- transition: transform 0.2s;
75
  }
76
-
77
- .page-result:hover {
78
- transform: translateY(-2px);
79
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
80
  }
81
-
82
- .page-result h4 {
83
- margin-top: 0;
84
- color: var(--primary);
85
- border-bottom: 2px solid #f1f3f5;
86
- padding-bottom: 0.5rem;
87
  }
88
-
89
- #resultsContainer {
90
- max-height: 600px;
91
- overflow-y: auto;
92
- margin-top: 20px;
93
- padding-right: 10px;
94
  }
95
-
96
- .status-text {
 
 
 
 
 
97
  text-align: center;
98
- margin: 10px 0;
99
- font-weight: 500;
100
- color: #495057;
101
- }
102
-
103
- button {
104
- background-color: var(--primary);
105
- transition: all 0.2s;
106
  }
107
-
108
- button:hover {
109
- background-color: var(--primary-hover);
110
- transform: translateY(-2px);
111
- }
112
-
113
- #cancelProcessingBtn {
114
- background-color: #e63946;
115
  }
116
-
117
- #cancelProcessingBtn:hover {
118
- background-color: #d62828;
119
  }
120
-
121
- pre {
122
- background-color: #f8f9fa;
123
- border-radius: 5px;
 
 
124
  padding: 10px;
125
- font-size: 0.9rem;
 
126
  }
127
-
128
- details summary {
129
- cursor: pointer;
130
- color: var(--primary);
131
- font-weight: 500;
132
  }
133
-
134
- footer {
135
- text-align: center;
136
- margin-top: 2rem;
137
- padding: 1rem 0;
138
- color: #6c757d;
139
- font-size: 0.9rem;
140
- }
141
-
142
  .logo {
143
  font-weight: bold;
144
- font-size: 1.2rem;
145
- color: white;
146
- text-decoration: none;
147
  }
148
-
149
  .logo span {
150
- color: var(--secondary);
151
- }
152
-
153
- .upload-container {
154
- border: 2px dashed #ced4da;
155
- border-radius: 8px;
156
- padding: 2rem;
157
- text-align: center;
158
- transition: all 0.2s;
159
- cursor: pointer;
160
- margin-bottom: 1rem;
161
- }
162
-
163
- .upload-container:hover {
164
- border-color: var(--primary);
165
- background-color: rgba(67, 97, 238, 0.05);
166
- }
167
-
168
- .upload-icon {
169
- font-size: 2rem;
170
- color: var(--primary);
171
- margin-bottom: 1rem;
172
- }
173
-
174
- .export-button {
175
- display: none;
176
- margin-top: 20px;
177
- }
178
-
179
- @media (max-width: 768px) {
180
- article {
181
- padding: 1rem;
182
- }
183
  }
184
  </style>
185
  </head>
186
  <body>
187
- <header class="container">
188
- <div style="text-align: center;">
189
- <a href="#" class="logo">UFast<span>Pro</span> OCR Technology</a>
190
- <p>Advanced PDF Text Recognition System</p>
 
191
  </div>
192
- </header>
193
-
194
- <main class="container">
195
- <article>
196
- <h3 style="text-align: center; margin-top: 0;">PDF Text Extraction</h3>
197
- <p style="text-align: center;">Upload any PDF document to extract text from all pages using our advanced OCR technology.</p>
198
-
199
- <div class="upload-container" id="uploadContainer">
200
- <div class="upload-icon">📄</div>
201
- <p>Drag & drop your PDF here or click to browse</p>
202
- <input type="file" id="pdfFile" name="pdfFile" accept=".pdf" style="display: none;">
203
  </div>
204
-
205
- <div class="progress-container" id="progressContainer" hidden>
206
- <div class="progress-bar" id="progressBar"></div>
207
- <p class="status-text" id="statusText">Ready to process</p>
 
 
 
 
 
 
 
 
 
 
208
  </div>
 
 
 
 
 
 
209
 
210
- <div style="display: flex; gap: 10px; justify-content: center;">
211
- <button id="startProcessingBtn" disabled>Start Processing</button>
212
- <button id="cancelProcessingBtn" hidden>Cancel Processing</button>
213
  </div>
214
 
215
- <button id="exportAllTextBtn" class="export-button">Export All Text</button>
216
- </article>
217
-
218
- <div id="resultsContainer"></div>
219
- </main>
220
-
221
- <footer class="container">
222
- <p>&copy; 2025 UFastPro OCR Technology. All rights reserved.</p>
223
- </footer>
 
 
 
 
 
 
 
 
 
 
 
224
 
225
  <script>
226
- // Set up PDF.js worker
227
  pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';
228
 
229
- // API keys pool
230
- const API_KEYS = [
231
- '32769fb369mshfdf6f5e28e26674p1f3764jsn2a31085a1fc7',
232
- '091dbfbf0emsh6606eb2165cfd97p191257jsnd017aa229e09',
233
- '2175cafd75msh4465ea00b022145p1e062ajsn05e78cebc495',
234
- 'eb11693cddmshb8bd157e05b74acp1f6aa4jsn4369fa546e55',
235
- 'e9c1cd0db3mshb54648cf3400243p1d0a2bjsnf0d54e43fca2',
236
- 'fdb95540bamsh786e3ec8083bb22p1b4c1ajsn44fecbb812bb'
237
- ];
238
 
239
- // Get random API key from the pool
240
- function getRandomApiKey() {
241
- const randomIndex = Math.floor(Math.random() * API_KEYS.length);
242
- return API_KEYS[randomIndex];
243
- }
244
 
245
- const RAPIDAPI_HOST = 'ocr43.p.rapidapi.com';
246
- const API_URL = 'https://ocr43.p.rapidapi.com/v1/results';
247
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  document.addEventListener('DOMContentLoaded', function() {
249
- const pdfFileInput = document.getElementById('pdfFile');
250
- const uploadContainer = document.getElementById('uploadContainer');
251
- const startProcessingBtn = document.getElementById('startProcessingBtn');
252
- const cancelProcessingBtn = document.getElementById('cancelProcessingBtn');
253
- const progressContainer = document.getElementById('progressContainer');
254
- const progressBar = document.getElementById('progressBar');
255
- const statusText = document.getElementById('statusText');
256
- const resultsContainer = document.getElementById('resultsContainer');
257
- const exportAllTextBtn = document.getElementById('exportAllTextBtn');
 
 
 
 
 
 
258
 
259
- let pdfDocument = null;
260
- let isProcessing = false;
261
- let shouldCancel = false;
262
- let allExtractedText = [];
263
 
264
- // Event listeners
265
- pdfFileInput.addEventListener('change', handlePdfSelection);
266
- uploadContainer.addEventListener('click', () => pdfFileInput.click());
267
- uploadContainer.addEventListener('dragover', (e) => {
268
- e.preventDefault();
269
- uploadContainer.style.borderColor = 'var(--primary)';
270
- uploadContainer.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
271
- });
272
- uploadContainer.addEventListener('dragleave', (e) => {
273
- e.preventDefault();
274
- uploadContainer.style.borderColor = '#ced4da';
275
- uploadContainer.style.backgroundColor = '';
276
- });
277
- uploadContainer.addEventListener('drop', (e) => {
278
- e.preventDefault();
279
- uploadContainer.style.borderColor = '#ced4da';
280
- uploadContainer.style.backgroundColor = '';
281
 
282
- if (e.dataTransfer.files.length) {
283
- pdfFileInput.files = e.dataTransfer.files;
284
- handlePdfSelection({ target: { files: e.dataTransfer.files } });
 
 
 
285
  }
286
- });
287
- startProcessingBtn.addEventListener('click', startProcessing);
288
- cancelProcessingBtn.addEventListener('click', cancelProcessing);
289
- exportAllTextBtn.addEventListener('click', exportAllText);
290
-
291
- // Handle PDF file selection
292
- async function handlePdfSelection(event) {
293
- const file = event.target.files[0];
294
- if (!file || file.type !== 'application/pdf') {
295
- alert('Please select a valid PDF file.');
296
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  }
298
 
299
- try {
300
- // Update UI to show loading
301
- statusText.textContent = 'Loading PDF...';
302
- progressContainer.hidden = false;
303
- progressBar.style.width = '10%';
 
 
 
 
 
 
 
304
 
305
- // Load the PDF file
306
- const arrayBuffer = await file.arrayBuffer();
307
- pdfDocument = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
 
 
 
 
308
 
309
- // Enable the start button and update status
310
- startProcessingBtn.disabled = false;
311
- statusText.textContent = `PDF loaded with ${pdfDocument.numPages} pages. Click "Start Processing" to begin.`;
312
- progressBar.style.width = '20%';
313
- } catch (error) {
314
- console.error('Error loading PDF:', error);
315
- alert('Error loading PDF file. Please try again.');
316
- progressContainer.hidden = true;
317
  }
 
 
 
 
318
  }
319
-
320
- // Start processing the PDF
321
- async function startProcessing() {
322
- if (!pdfDocument || isProcessing) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
- isProcessing = true;
325
- shouldCancel = false;
326
- startProcessingBtn.hidden = true;
327
- cancelProcessingBtn.hidden = false;
328
- resultsContainer.innerHTML = '';
329
- allExtractedText = [];
330
- exportAllTextBtn.style.display = 'none';
331
 
332
- const totalPages = pdfDocument.numPages;
 
 
333
 
334
- for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
335
- if (shouldCancel) {
336
- statusText.textContent = 'Processing cancelled.';
337
- break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  }
339
-
340
- // Update progress
341
- updateProgress(pageNum, totalPages);
342
-
343
- try {
344
- // Process the current page
345
- await processPage(pageNum, totalPages);
346
- } catch (error) {
347
- console.error(`Error processing page ${pageNum}:`, error);
348
- addPageResult(pageNum, `Error: ${error.message}`, null);
349
- allExtractedText.push(`[PAGE ${pageNum}]\nError: ${error.message}\n`);
350
  }
351
  }
352
-
353
- // Reset UI after processing
354
- if (!shouldCancel) {
355
- statusText.textContent = 'Processing complete!';
356
- progressBar.style.width = '100%';
357
- exportAllTextBtn.style.display = 'block';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  }
359
-
360
- isProcessing = false;
361
- startProcessingBtn.hidden = false;
362
- cancelProcessingBtn.hidden = true;
 
 
 
 
 
 
 
 
 
 
 
 
363
  }
364
 
365
- // Cancel the processing
366
- function cancelProcessing() {
367
- shouldCancel = true;
368
- statusText.textContent = 'Cancelling...';
369
- }
370
 
371
- // Update progress bar and status text
372
- function updateProgress(current, total) {
373
- const percentage = 20 + ((current / total) * 80); // Start at 20% (after loading)
374
- progressBar.style.width = `${percentage}%`;
375
- statusText.textContent = `Processing page ${current} of ${total}`;
376
  }
377
 
378
- // Export all extracted text
379
- function exportAllText() {
380
- if (allExtractedText.length === 0) return;
381
-
382
- const combinedText = allExtractedText.join('\n\n');
383
- const blob = new Blob([combinedText], { type: 'text/plain' });
384
- const url = URL.createObjectURL(blob);
385
-
386
  const a = document.createElement('a');
387
- a.href = url;
388
- a.download = 'extracted_text.txt';
389
  document.body.appendChild(a);
390
  a.click();
391
  document.body.removeChild(a);
392
- URL.revokeObjectURL(url);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  }
394
 
395
- // Process a single page
396
- async function processPage(pageNum, totalPages) {
397
- // Get the page
398
- const page = await pdfDocument.getPage(pageNum);
 
 
 
399
 
400
- // Create a canvas for the page (not displayed but needed for processing)
401
- const viewport = page.getViewport({ scale: 1.5 });
402
- const canvas = document.createElement('canvas');
403
- const context = canvas.getContext('2d');
404
- canvas.height = viewport.height;
405
- canvas.width = viewport.width;
 
 
 
 
 
 
406
 
407
- // Render the page to the canvas
408
- await page.render({
409
- canvasContext: context,
410
- viewport: viewport
411
- }).promise;
412
 
413
- // Convert canvas to blob
414
- const blob = await new Promise(resolve => {
415
- canvas.toBlob(resolve, 'image/png');
416
- });
 
 
 
 
 
417
 
418
- // Get a random API key for this request
419
- const randomApiKey = getRandomApiKey();
 
 
 
 
 
 
 
 
 
 
420
 
421
- // Send the image to OCR API
422
  const formData = new FormData();
423
- formData.append('image', blob, `page-${pageNum}.png`);
424
 
425
- const response = await fetch(API_URL, {
 
426
  method: 'POST',
427
  headers: {
428
- 'X-RapidAPI-Key': randomApiKey,
429
- 'X-RapidAPI-Host': RAPIDAPI_HOST
430
  },
431
  body: formData
432
  });
433
 
434
- const data = await response.json();
435
-
436
- // Extract text from the response
437
- let extractedText = '';
438
- try {
439
- extractedText = data.results[0].entities[0].objects[0].entities[0].text;
440
- } catch (error) {
441
- extractedText = 'No text could be extracted from this page.';
442
  }
443
 
444
- // Store the extracted text
445
- allExtractedText.push(`[PAGE ${pageNum}]\n${extractedText}`);
446
 
447
- // Add the result to the UI
448
- addPageResult(pageNum, extractedText, data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  }
450
 
451
- // Add a page result to the UI
452
- function addPageResult(pageNum, text, rawData) {
453
- const pageResult = document.createElement('div');
454
- pageResult.className = 'page-result';
455
-
456
- // Add page header
457
- const header = document.createElement('h4');
458
- header.textContent = `Page ${pageNum}`;
459
- pageResult.appendChild(header);
460
-
461
- // Add extracted text
462
- const textHeader = document.createElement('h5');
463
- textHeader.textContent = 'Extracted Text:';
464
- pageResult.appendChild(textHeader);
465
-
466
- const textContent = document.createElement('pre');
467
- textContent.style.whiteSpace = 'pre-wrap';
468
- textContent.textContent = text || 'No text extracted';
469
- pageResult.appendChild(textContent);
470
-
471
- // Add copy button
472
- const copyButton = document.createElement('button');
473
- copyButton.textContent = 'Copy Text';
474
- copyButton.setAttribute('aria-label', 'Copy text to clipboard');
475
- copyButton.style.marginBottom = '10px';
476
- copyButton.addEventListener('click', () => {
477
- navigator.clipboard.writeText(text || '')
478
- .then(() => {
479
- const originalText = copyButton.textContent;
480
- copyButton.textContent = 'Copied!';
481
- setTimeout(() => {
482
- copyButton.textContent = originalText;
483
- }, 2000);
484
- })
485
- .catch(err => console.error('Failed to copy text:', err));
486
- });
487
- pageResult.appendChild(copyButton);
488
-
489
- // Add raw data toggle (optional, can be removed if not needed)
490
- if (rawData) {
491
- const rawToggle = document.createElement('details');
492
- const rawSummary = document.createElement('summary');
493
- rawSummary.textContent = 'Show Raw API Response';
494
- rawToggle.appendChild(rawSummary);
495
-
496
- const rawContent = document.createElement('pre');
497
- rawContent.style.maxHeight = '200px';
498
- rawContent.style.overflow = 'auto';
499
- rawContent.textContent = JSON.stringify(rawData, null, 2);
500
- rawToggle.appendChild(rawContent);
501
-
502
- pageResult.appendChild(rawToggle);
503
- }
504
-
505
- // Add to results container
506
- resultsContainer.appendChild(pageResult);
507
-
508
- // Scroll to the new result
509
- pageResult.scrollIntoView({ behavior: 'smooth', block: 'end' });
510
  }
511
- });
 
 
 
 
 
 
 
 
 
 
512
  </script>
513
  </body>
514
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="ar" dir="rtl">
3
  <head>
 
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>موندو لينجوا - نظام OCR</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
8
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
9
  <style>
 
 
 
 
 
 
 
10
  body {
11
+ font-family: Arial, sans-serif;
12
+ margin: 0;
13
+ padding: 20px;
14
  background-color: #f8f9fa;
15
  }
16
+ .header {
17
+ background-color: #3b82f6;
 
 
 
 
 
 
 
18
  color: white;
19
+ padding: 15px;
20
+ border-radius: 10px;
21
+ margin-bottom: 20px;
22
+ text-align: center;
 
 
 
23
  }
24
+ .container {
25
+ max-width: 800px;
26
+ margin: 0 auto;
 
27
  }
28
+ .card {
 
 
 
29
  background-color: white;
30
  border-radius: 10px;
31
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
32
+ padding: 20px;
33
+ margin-bottom: 20px;
34
  }
35
+ .pdf-grid {
36
+ display: grid;
37
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
38
+ gap: 10px;
39
+ margin-top: 15px;
40
  }
41
+ .pdf-page {
42
+ border: 1px solid #ddd;
 
 
 
43
  border-radius: 5px;
44
+ padding: 5px;
45
+ position: relative;
46
+ cursor: pointer;
47
+ transition: all 0.2s;
 
 
 
 
 
 
 
 
48
  }
49
+ .pdf-page:hover {
50
+ transform: translateY(-3px);
51
+ box-shadow: 0 3px 10px rgba(0,0,0,0.1);
 
52
  }
53
+ .pdf-page.selected {
54
+ border: 2px solid #3b82f6;
 
 
 
 
55
  }
56
+ .pdf-page img {
57
+ width: 100%;
58
+ height: auto;
59
+ border-radius: 3px;
 
 
60
  }
61
+ .page-number {
62
+ position: absolute;
63
+ bottom: 0;
64
+ left: 0;
65
+ right: 0;
66
+ background-color: rgba(0,0,0,0.6);
67
+ color: white;
68
  text-align: center;
69
+ font-size: 12px;
70
+ padding: 2px;
 
 
 
 
 
 
71
  }
72
+ .btn-primary {
73
+ background-color: #3b82f6;
74
+ border-color: #3b82f6;
 
 
 
 
 
75
  }
76
+ .btn-success {
77
+ background-color: #10b981;
78
+ border-color: #10b981;
79
  }
80
+ .result-text {
81
+ max-height: 300px;
82
+ overflow-y: auto;
83
+ white-space: pre-wrap;
84
+ direction: rtl;
85
+ border: 1px solid #ddd;
86
  padding: 10px;
87
+ border-radius: 5px;
88
+ background-color: #f8f9fa;
89
  }
90
+ .spinner-border {
91
+ width: 1.5rem;
92
+ height: 1.5rem;
93
+ margin-left: 0.5rem;
 
94
  }
 
 
 
 
 
 
 
 
 
95
  .logo {
96
  font-weight: bold;
97
+ font-size: 24px;
98
+ display: inline-block;
99
+ margin-bottom: 5px;
100
  }
 
101
  .logo span {
102
+ color: #bfdbfe;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
104
  </style>
105
  </head>
106
  <body>
107
+ <div class="container">
108
+ <div class="header">
109
+ <div class="logo">مـوندو <span>لينجـوا</span></div>
110
+ <h1>نظام التعرف الضوئي على النصوص</h1>
111
+ <p class="mb-0">استخراج النصوص من الصور والملفات متعددة الصفحات</p>
112
  </div>
113
+
114
+ <!-- بطاقة تحميل الملف -->
115
+ <div class="card">
116
+ <h3>تحميل الملف</h3>
117
+ <div class="mb-3">
118
+ <input type="file" class="form-control" id="fileInput" accept=".pdf,.jpg,.jpeg,.png">
119
+ <div class="form-text">يمكنك تحميل ملف PDF (حتى 30 صفحة) أو صورة</div>
 
 
 
 
120
  </div>
121
+ <button id="processBtn" class="btn btn-primary">معالجة الملف</button>
122
+ </div>
123
+
124
+ <!-- عرض حالة المعالجة -->
125
+ <div id="processingStatus" class="card d-none">
126
+ <h3>حالة المعالجة</h3>
127
+ <div class="alert alert-info">
128
+ <div class="d-flex align-items-center">
129
+ <div class="spinner-border text-primary" role="status"></div>
130
+ <span id="statusText" class="ms-2">جاري معالجة الملف...</span>
131
+ </div>
132
+ </div>
133
+ <div class="progress mt-2">
134
+ <div id="progressBar" class="progress-bar" role="progressbar" style="width: 0%"></div>
135
  </div>
136
+ </div>
137
+
138
+ <!-- عرض صفحات PDF -->
139
+ <div id="pdfPagesCard" class="card d-none">
140
+ <h3>صفحات الملف</h3>
141
+ <p>اختر الصفحات التي تريد معالجتها (انقر للتحديد)</p>
142
 
143
+ <div class="mb-3">
144
+ <button id="selectAllBtn" class="btn btn-sm btn-outline-primary me-2">تحديد الكل</button>
145
+ <button id="deselectAllBtn" class="btn btn-sm btn-outline-secondary">إلغاء تحديد الكل</button>
146
  </div>
147
 
148
+ <div id="pdfPagesContainer" class="pdf-grid"></div>
149
+
150
+ <div class="mt-3">
151
+ <button id="extractImagesBtn" class="btn btn-success me-2">تحويل إلى صور</button>
152
+ <button id="extractTextBtn" class="btn btn-primary">استخراج النص</button>
153
+ </div>
154
+ </div>
155
+
156
+ <!-- عرض النتائج -->
157
+ <div id="resultsCard" class="card d-none">
158
+ <h3>النص المستخرج</h3>
159
+ <div id="resultText" class="result-text mt-3">
160
+ لم يتم استخراج نص بعد.
161
+ </div>
162
+ <div class="mt-3">
163
+ <button id="copyTextBtn" class="btn btn-outline-primary me-2">نسخ النص</button>
164
+ <button id="downloadTextBtn" class="btn btn-success">تنزيل النص</button>
165
+ </div>
166
+ </div>
167
+ </div>
168
 
169
  <script>
170
+ // تهيئة PDF.js
171
  pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';
172
 
173
+ // إعدادات API4AI OCR
174
+ const RAPIDAPI_KEY = 'eb11693cddmshb8bd157e05b74acp1f6aa4jsn4369fa546e55';
175
+ const OCR_API_URL = 'https://ocr43.p.rapidapi.com/v1/results';
 
 
 
 
 
 
176
 
177
+ // متغيرات عامة
178
+ let documentPages = [];
179
+ let selectedPages = [];
180
+ let extractedTexts = [];
 
181
 
182
+ // عناصر DOM
183
+ const fileInput = document.getElementById('fileInput');
184
+ const processBtn = document.getElementById('processBtn');
185
+ const processingStatus = document.getElementById('processingStatus');
186
+ const statusText = document.getElementById('statusText');
187
+ const progressBar = document.getElementById('progressBar');
188
+ const pdfPagesCard = document.getElementById('pdfPagesCard');
189
+ const pdfPagesContainer = document.getElementById('pdfPagesContainer');
190
+ const selectAllBtn = document.getElementById('selectAllBtn');
191
+ const deselectAllBtn = document.getElementById('deselectAllBtn');
192
+ const extractImagesBtn = document.getElementById('extractImagesBtn');
193
+ const extractTextBtn = document.getElementById('extractTextBtn');
194
+ const resultsCard = document.getElementById('resultsCard');
195
+ const resultText = document.getElementById('resultText');
196
+ const copyTextBtn = document.getElementById('copyTextBtn');
197
+ const downloadTextBtn = document.getElementById('downloadTextBtn');
198
+
199
+ // إضافة مستمعات الأحداث
200
  document.addEventListener('DOMContentLoaded', function() {
201
+ processBtn.addEventListener('click', processFile);
202
+ selectAllBtn.addEventListener('click', selectAllPages);
203
+ deselectAllBtn.addEventListener('click', deselectAllPages);
204
+ extractImagesBtn.addEventListener('click', extractImages);
205
+ extractTextBtn.addEventListener('click', extractText);
206
+ copyTextBtn.addEventListener('click', copyText);
207
+ downloadTextBtn.addEventListener('click', downloadText);
208
+ });
209
+
210
+ // معالجة الملف
211
+ async function processFile() {
212
+ if (!fileInput.files || fileInput.files.length === 0) {
213
+ alert('الرجاء اختيار ملف أولاً');
214
+ return;
215
+ }
216
 
217
+ const file = fileInput.files[0];
 
 
 
218
 
219
+ // إظهار حالة المعالجة
220
+ processingStatus.classList.remove('d-none');
221
+ statusText.textContent = 'جاري معالجة الملف...';
222
+ progressBar.style.width = '0%';
223
+
224
+ // إخفاء بطاقات أخرى
225
+ pdfPagesCard.classList.add('d-none');
226
+ resultsCard.classList.add('d-none');
227
+
228
+ // إفراغ مصفوفات الصفحا��
229
+ documentPages = [];
230
+ selectedPages = [];
231
+ extractedTexts = [];
232
+
233
+ try {
234
+ const fileType = file.name.split('.').pop().toLowerCase();
 
235
 
236
+ if (fileType === 'pdf') {
237
+ await processPdf(file);
238
+ } else if (['jpg', 'jpeg', 'png'].includes(fileType)) {
239
+ await processImage(file);
240
+ } else {
241
+ throw new Error('نوع الملف غير مدعوم. يرجى اختيار ملف PDF أو صورة.');
242
  }
243
+
244
+ // إظهار بطاقة صفحات PDF
245
+ pdfPagesCard.classList.remove('d-none');
246
+
247
+ // إخفاء حالة المعالجة
248
+ processingStatus.classList.add('d-none');
249
+
250
+ } catch (error) {
251
+ console.error('Error processing file:', error);
252
+ statusText.textContent = `خطأ: ${error.message}`;
253
+ // لا نخفي حالة المعالجة لإظهار الخطأ
254
+ }
255
+ }
256
+
257
+ // معالجة ملف PDF
258
+ async function processPdf(file) {
259
+ try {
260
+ // تحميل ملف PDF
261
+ const arrayBuffer = await file.arrayBuffer();
262
+ const pdfDoc = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
263
+
264
+ // التحقق من عدد الصفحات
265
+ const numPages = pdfDoc.numPages;
266
+ if (numPages > 30) {
267
+ alert('هذا الملف يحتوي على أكثر من 30 صفحة. سيتم معالجة أول 30 صفحة فقط.');
268
  }
269
 
270
+ // مسح حاوية الصفحات
271
+ pdfPagesContainer.innerHTML = '';
272
+
273
+ // عرض أول 30 صفحة كصور مصغرة
274
+ const maxPages = Math.min(numPages, 30);
275
+ for (let i = 1; i <= maxPages; i++) {
276
+ // تحديث شريط التقدم
277
+ progressBar.style.width = `${(i / maxPages) * 100}%`;
278
+ statusText.textContent = `جاري معالجة الصفحة ${i} من ${maxPages}...`;
279
+
280
+ // تحويل الصفحة إلى صورة
281
+ const pageImage = await convertPdfPageToImage(pdfDoc, i);
282
 
283
+ // إضافة الصفحة إلى المصفوفة
284
+ documentPages.push({
285
+ pageNumber: i,
286
+ imageData: pageImage.imageData,
287
+ width: pageImage.width,
288
+ height: pageImage.height
289
+ });
290
 
291
+ // إنشاء عنصر للصفحة في واجهة المستخدم
292
+ createPageElement(pageImage.imageData, i);
 
 
 
 
 
 
293
  }
294
+
295
+ } catch (error) {
296
+ console.error('Error processing PDF:', error);
297
+ throw error;
298
  }
299
+ }
300
+
301
+ // معالجة ملف صورة
302
+ async function processImage(file) {
303
+ try {
304
+ // قراءة الصورة كـ Data URL
305
+ const imageData = await readFileAsDataURL(file);
306
+
307
+ // إنشاء صورة لقياس أبعادها
308
+ const img = new Image();
309
+ await new Promise((resolve, reject) => {
310
+ img.onload = resolve;
311
+ img.onerror = reject;
312
+ img.src = imageData;
313
+ });
314
+
315
+ // مسح حاوية الصفحات
316
+ pdfPagesContainer.innerHTML = '';
317
+
318
+ // إضافة الصورة إلى المصفوفة
319
+ documentPages = [{
320
+ pageNumber: 1,
321
+ imageData: imageData,
322
+ width: img.width,
323
+ height: img.height
324
+ }];
325
 
326
+ // إنشاء عنصر للصورة في واجهة المستخدم
327
+ createPageElement(imageData, 1);
 
 
 
 
 
328
 
329
+ // تحديث شريط التقدم
330
+ progressBar.style.width = '100%';
331
+ statusText.textContent = 'تمت معالجة الصورة بنجاح';
332
 
333
+ } catch (error) {
334
+ console.error('Error processing image:', error);
335
+ throw error;
336
+ }
337
+ }
338
+
339
+ // تحويل صفحة PDF إلى صورة
340
+ async function convertPdfPageToImage(pdfDoc, pageNumber, scale = 1.5) {
341
+ try {
342
+ // الحصول على الصفحة من مستند PDF
343
+ const page = await pdfDoc.getPage(pageNumber);
344
+
345
+ // إنشاء عنصر canvas لرسم الصفحة
346
+ const canvas = document.createElement('canvas');
347
+ const context = canvas.getContext('2d');
348
+
349
+ // ضبط حجم الـ canvas بناءً على حجم الصفحة ومقياس التكبير
350
+ const viewport = page.getViewport({ scale });
351
+ canvas.width = viewport.width;
352
+ canvas.height = viewport.height;
353
+
354
+ // رسم الصفحة على الـ canvas
355
+ await page.render({
356
+ canvasContext: context,
357
+ viewport: viewport
358
+ }).promise;
359
+
360
+ // تحويل الـ canvas إلى صورة بصيغة PNG
361
+ return {
362
+ imageData: canvas.toDataURL('image/png'),
363
+ width: viewport.width,
364
+ height: viewport.height,
365
+ pageNumber: pageNumber
366
+ };
367
+ } catch (error) {
368
+ console.error(`Error converting PDF page ${pageNumber} to image:`, error);
369
+ throw error;
370
+ }
371
+ }
372
+
373
+ // إنشاء عنصر للصفحة في واجهة المستخدم
374
+ function createPageElement(imageData, pageNumber) {
375
+ const pageDiv = document.createElement('div');
376
+ pageDiv.className = 'pdf-page';
377
+ pageDiv.dataset.page = pageNumber;
378
+
379
+ const img = document.createElement('img');
380
+ img.src = imageData;
381
+ img.alt = `صفحة ${pageNumber}`;
382
+
383
+ const pageNumberDiv = document.createElement('div');
384
+ pageNumberDiv.className = 'page-number';
385
+ pageNumberDiv.textContent = `صفحة ${pageNumber}`;
386
+
387
+ pageDiv.appendChild(img);
388
+ pageDiv.appendChild(pageNumberDiv);
389
+
390
+ // إضافة حدث النقر للتحديد
391
+ pageDiv.addEventListener('click', function() {
392
+ this.classList.toggle('selected');
393
+
394
+ // تحديث مصفوفة الصفحات المحددة
395
+ const page = parseInt(this.dataset.page);
396
+ if (this.classList.contains('selected')) {
397
+ if (!selectedPages.includes(page)) {
398
+ selectedPages.push(page);
399
  }
400
+ } else {
401
+ const index = selectedPages.indexOf(page);
402
+ if (index > -1) {
403
+ selectedPages.splice(index, 1);
 
 
 
 
 
 
 
404
  }
405
  }
406
+ });
407
+
408
+ pdfPagesContainer.appendChild(pageDiv);
409
+ }
410
+
411
+ // قراءة ملف كـ Data URL
412
+ function readFileAsDataURL(file) {
413
+ return new Promise((resolve, reject) => {
414
+ const reader = new FileReader();
415
+ reader.onload = e => resolve(e.target.result);
416
+ reader.onerror = reject;
417
+ reader.readAsDataURL(file);
418
+ });
419
+ }
420
+
421
+ // تحديد كل الصفحات
422
+ function selectAllPages() {
423
+ document.querySelectorAll('.pdf-page').forEach(page => {
424
+ page.classList.add('selected');
425
+ const pageNumber = parseInt(page.dataset.page);
426
+ if (!selectedPages.includes(pageNumber)) {
427
+ selectedPages.push(pageNumber);
428
  }
429
+ });
430
+ }
431
+
432
+ // إلغاء تحديد كل الصفحات
433
+ function deselectAllPages() {
434
+ document.querySelectorAll('.pdf-page').forEach(page => {
435
+ page.classList.remove('selected');
436
+ });
437
+ selectedPages = [];
438
+ }
439
+
440
+ // استخراج الصور
441
+ function extractImages() {
442
+ if (documentPages.length === 0) {
443
+ alert('لا توجد صفحات للمعالجة');
444
+ return;
445
  }
446
 
447
+ // اختيار الصفحات المحددة فقط أو كل الصفحات إذا لم يتم تحديد أي صفحة
448
+ const pagesToExtract = selectedPages.length > 0
449
+ ? documentPages.filter(page => selectedPages.includes(page.pageNumber))
450
+ : documentPages;
 
451
 
452
+ if (pagesToExtract.length === 0) {
453
+ alert('الرجاء تحديد صفحة واحدة على الأقل');
454
+ return;
 
 
455
  }
456
 
457
+ // إنشاء وتنزيل كل صورة
458
+ pagesToExtract.forEach(page => {
 
 
 
 
 
 
459
  const a = document.createElement('a');
460
+ a.href = page.imageData;
461
+ a.download = `mondo_lingua_page_${page.pageNumber}.png`;
462
  document.body.appendChild(a);
463
  a.click();
464
  document.body.removeChild(a);
465
+ });
466
+
467
+ // إظهار رسالة نجاح
468
+ alert(`تم استخراج ${pagesToExtract.length} صورة بنجاح`);
469
+ }
470
+
471
+ // استخراج النص
472
+ async function extractText() {
473
+ if (documentPages.length === 0) {
474
+ alert('لا توجد صفحات للمعالجة');
475
+ return;
476
+ }
477
+
478
+ // اختيار الصفحات المحددة فقط أو كل الصفحات إذا لم يتم تحديد أي صفحة
479
+ const pagesToProcess = selectedPages.length > 0
480
+ ? documentPages.filter(page => selectedPages.includes(page.pageNumber))
481
+ : documentPages;
482
+
483
+ if (pagesToProcess.length === 0) {
484
+ alert('الرجاء تحديد صفحة واحدة على الأقل');
485
+ return;
486
  }
487
 
488
+ // إظهار حالة المعالجة
489
+ processingStatus.classList.remove('d-none');
490
+ statusText.textContent = 'جاري استخراج النص...';
491
+ progressBar.style.width = '0%';
492
+
493
+ try {
494
+ extractedTexts = [];
495
 
496
+ // معالجة كل صفحة
497
+ for (let i = 0; i < pagesToProcess.length; i++) {
498
+ const page = pagesToProcess[i];
499
+
500
+ // تحديث التقدم
501
+ progressBar.style.width = `${((i + 1) / pagesToProcess.length) * 100}%`;
502
+ statusText.textContent = `جاري معالجة الصفحة ${i + 1} من ${pagesToProcess.length}...`;
503
+
504
+ // استخراج النص من الصورة باستخدام OCR API
505
+ const pageText = await extractTextFromImage(page.imageData, page.pageNumber);
506
+ extractedTexts.push(pageText);
507
+ }
508
 
509
+ // جمع النصوص المستخرجة
510
+ const combinedText = extractedTexts.join('\n\n');
 
 
 
511
 
512
+ // عرض النص المستخرج
513
+ resultText.textContent = combinedText;
514
+ resultsCard.classList.remove('d-none');
515
+
516
+ // إخفاء حالة المعالجة
517
+ processingStatus.classList.add('d-none');
518
+
519
+ // التمرير إلى النتائج
520
+ resultsCard.scrollIntoView({ behavior: 'smooth' });
521
 
522
+ } catch (error) {
523
+ console.error('Error extracting text:', error);
524
+ statusText.textContent = `خطأ: ${error.message}`;
525
+ }
526
+ }
527
+
528
+ // استخراج النص من صورة باستخدام API4AI OCR
529
+ async function extractTextFromImage(imageData, pageNumber) {
530
+ try {
531
+ // تحويل Data URL إلى Blob
532
+ const response = await fetch(imageData);
533
+ const blob = await response.blob();
534
 
535
+ // إنشاء FormData وإضافة الصورة
536
  const formData = new FormData();
537
+ formData.append('image', blob, `page_${pageNumber}.png`);
538
 
539
+ // طلب OCR
540
+ const ocrResponse = await fetch(OCR_API_URL, {
541
  method: 'POST',
542
  headers: {
543
+ 'X-RapidAPI-Key': RAPIDAPI_KEY,
544
+ 'X-RapidAPI-Host': 'ocr43.p.rapidapi.com'
545
  },
546
  body: formData
547
  });
548
 
549
+ if (!ocrResponse.ok) {
550
+ throw new Error(`فشل في طلب OCR: ${ocrResponse.status}`);
 
 
 
 
 
 
551
  }
552
 
553
+ const data = await ocrResponse.json();
 
554
 
555
+ try {
556
+ // استخراج النص من الاستجابة
557
+ const text = data.results[0].entities[0].objects[0].entities[0].text;
558
+ return `=== صفحة ${pageNumber} ===\n${text}`;
559
+ } catch (e) {
560
+ console.error('Error parsing OCR response:', e);
561
+ return `=== صفحة ${pageNumber} ===\n[خطأ في معالجة النص]`;
562
+ }
563
+ } catch (error) {
564
+ console.error(`Error in OCR for page ${pageNumber}:`, error);
565
+ throw error;
566
+ }
567
+ }
568
+
569
+ // نسخ النص
570
+ function copyText() {
571
+ const text = resultText.textContent;
572
+ if (!text || text === 'لم يتم استخراج نص بعد.') {
573
+ alert('لا يوجد نص للنسخ');
574
+ return;
575
  }
576
 
577
+ navigator.clipboard.writeText(text)
578
+ .then(() => alert('تم نسخ النص بنجاح'))
579
+ .catch(err => alert('حدث خطأ أثناء نسخ النص: ' + err));
580
+ }
581
+
582
+ // تنزيل النص
583
+ function downloadText() {
584
+ const text = resultText.textContent;
585
+ if (!text || text === 'لم يتم استخراج نص بعد.') {
586
+ alert('لا يوجد نص للتنزيل');
587
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
  }
589
+
590
+ const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
591
+ const url = URL.createObjectURL(blob);
592
+ const a = document.createElement('a');
593
+ a.href = url;
594
+ a.download = 'mondo_lingua_ocr_text.txt';
595
+ document.body.appendChild(a);
596
+ a.click();
597
+ document.body.removeChild(a);
598
+ URL.revokeObjectURL(url);
599
+ }
600
  </script>
601
  </body>
602
  </html>