joermd commited on
Commit
788900f
·
verified ·
1 Parent(s): 5a6ee0d

Update ocr.html

Browse files
Files changed (1) hide show
  1. ocr.html +526 -439
ocr.html CHANGED
@@ -3,513 +3,600 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>نظام المقارنة والترجمة الخاص بشركة موندو لينجوا</title>
7
- <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
10
  <style>
11
- @keyframes gradient {
12
- 0% { background-position: 0% 50%; }
13
- 50% { background-position: 100% 50%; }
14
- 100% { background-position: 0% 50%; }
 
15
  }
16
- .animate-gradient {
17
- background-size: 200% 200%;
18
- animation: gradient 15s ease infinite;
 
 
 
 
19
  }
20
- .transition-all { transition: all 0.3s ease-in-out; }
21
- .animate-scale { transition: transform 0.2s ease-in-out; }
22
- .animate-scale:hover { transform: scale(1.02); }
23
- .file-drop-active { border-color: #3B82F6; background-color: rgba(59,130,246,0.1); }
24
- .text-comparison { line-height: 1.8; white-space: pre-wrap; }
25
- /* تمييز اختلاف الأرقام */
26
- .highlight-number {
27
- background-color: #FDE68A;
28
- padding: 0 2px;
29
- border-radius: 2px;
30
  }
31
- /* تمييز النصوص الناقصة */
32
- .highlight-missing {
33
- background-color: #BFDBFE;
34
- padding: 0 2px;
35
- border-radius: 2px;
 
36
  }
37
- /* تمييز اختلاف المعنى */
38
- .highlight-meaning {
39
- color: #EF4444;
40
- font-weight: bold;
41
- }
42
- /* تقسيم شاشة المعاينة إلى جزئين */
43
- .split-view {
44
  display: grid;
45
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
46
- gap: 1rem;
 
47
  }
48
- .result-section {
49
- border-top: 3px solid #E0E7FF;
50
- margin-top: 2rem;
51
- padding-top: 2rem;
 
 
 
52
  }
53
- /* تنسيق كل سطر مع رقم السطر */
54
- .line-item {
55
- display: flex;
56
- align-items: flex-start;
57
- margin-bottom: 0.5rem;
58
  }
59
- .line-number {
60
- width: 30px;
61
- font-weight: bold;
62
- color: #4B5563;
63
- flex-shrink: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
- .line-text {
66
- flex: 1;
 
67
  }
68
- /* تحسينات إضافية للواجهة */
69
- .card-hover {
70
- transition: all 0.3s ease;
71
  }
72
- .card-hover:hover {
73
- box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.1), 0 10px 10px -5px rgba(59, 130, 246, 0.04);
74
- transform: translateY(-2px);
 
 
 
 
 
 
75
  }
76
- .pulse-animation {
77
- animation: pulse 2s infinite;
 
 
78
  }
79
- @keyframes pulse {
80
- 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); }
81
- 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
82
- 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
 
 
 
 
83
  }
84
  </style>
85
  </head>
86
- <body class="bg-gradient-to-br from-gray-50 via-blue-50 to-indigo-50 min-h-screen">
87
- <div class="min-h-screen pb-12">
88
- <!-- Header -->
89
- <header class="bg-gradient-to-r from-blue-600 via-indigo-600 to-purple-600 animate-gradient text-white py-10 mb-10 shadow-xl">
90
- <div class="max-w-6xl mx-auto px-4">
91
- <h1 class="text-5xl font-bold text-center mb-4 animate-scale">نظام شركة موندو لينجوا</h1>
92
- <p class="text-center text-xl text-blue-100 opacity-90">مقارنة وتحليل النصوص – المصدر مرجع أساسي</p>
93
- </div>
94
- </header>
95
 
96
- <!-- Main Content -->
97
- <main class="max-w-6xl mx-auto px-4">
98
- <!-- قسم رفع الملفات -->
99
- <div class="bg-white rounded-2xl shadow-lg p-8 border border-gray-100 hover:shadow-xl transition-all animate-scale mb-8 card-hover">
100
- <h2 class="text-2xl font-bold mb-6 text-gray-800 flex items-center border-b pb-3">
101
- <i class="fas fa-file-upload text-blue-600 ml-2"></i> تحميل الملفات
102
- </h2>
103
- <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
104
- <!-- ملف السورس -->
105
- <div class="group border-2 border-dashed border-blue-200 rounded-xl p-8 text-center hover:border-blue-500 transition-colors duration-300 bg-blue-50 hover:bg-blue-100">
106
- <label class="cursor-pointer block">
107
- <input type="file" id="sourceFile" accept=".docx,.pdf" class="hidden">
108
- <i class="fas fa-file-upload text-5xl text-blue-500 mb-4"></i>
109
- <span class="text-lg text-blue-600 group-hover:text-blue-700">ملف السورس</span>
110
- </label>
111
- </div>
112
- <!-- ملف التارجت -->
113
- <div class="group border-2 border-dashed border-purple-200 rounded-xl p-8 text-center hover:border-purple-500 transition-colors duration-300 bg-purple-50 hover:bg-purple-100">
114
- <label class="cursor-pointer block">
115
- <input type="file" id="targetFile" accept=".docx,.pdf" class="hidden">
116
- <i class="fas fa-file-download text-5xl text-purple-500 mb-4"></i>
117
- <span class="text-lg text-purple-600 group-hover:text-purple-700">ملف التارجت</span>
118
- </label>
119
- </div>
120
- </div>
121
- <div id="processStatus" class="hidden mt-4">
122
- <div class="flex items-center justify-center space-x-3 bg-blue-100 rounded-xl p-4">
123
- <div class="animate-spin h-8 w-8 border-4 border-blue-600 rounded-full border-t-transparent"></div>
124
- <span class="text-lg text-blue-700">جارٍ معالجة الملف...</span>
125
- </div>
126
- </div>
127
  </div>
 
 
128
 
129
- <!-- إدخال النصوص يدويًا -->
130
- <div class="bg-white rounded-2xl shadow-lg p-8 border border-gray-100 hover:shadow-xl transition-all animate-scale mb-8 card-hover">
131
- <div class="space-y-6">
132
- <!-- النص المصدر -->
133
- <div class="group">
134
- <label class="block text-lg font-bold text-gray-700 mb-3 flex items-center">
135
- <i class="fas fa-language text-blue-600 ml-2"></i> النص المصدر
136
- </label>
137
- <textarea id="sourceText" dir="rtl" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-blue-200 focus:border-blue-400 transition-all resize-none text-lg" rows="6" placeholder="اكتب النص المصدر هنا..."></textarea>
138
- </div>
139
- <!-- النص الهدف -->
140
- <div class="group">
141
- <label class="block text-lg font-bold text-gray-700 mb-3 flex items-center">
142
- <i class="fas fa-language text-purple-600 ml-2"></i> النص الهدف
143
- </label>
144
- <textarea id="targetText" dir="ltr" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-purple-200 focus:border-purple-400 transition-all resize-none text-lg" rows="6" placeholder="اكتب النص الهدف هنا..."></textarea>
145
- </div>
146
  </div>
147
  </div>
 
 
 
 
148
 
149
- <!-- زر التحليل -->
150
- <button id="submitBtn" class="w-full bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-bold py-5 px-8 rounded-xl transition-all transform hover:scale-105 focus:ring-blue-200 text-xl shadow-lg hover:shadow-xl mb-8 pulse-animation">
151
- <div class="flex items-center justify-center">
152
- <i class="fas fa-sync-alt ml-2"></i> تحليل النصوص
153
- </div>
154
- </button>
155
-
156
- <!-- شاشة نتائج التحليل (المعاينة المقسمة) -->
157
- <div id="resultSection" class="bg-white rounded-2xl shadow-lg p-8 border border-gray-100 hover:shadow-xl transition-all animate-scale mb-8 hidden card-hover">
158
- <h2 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-3 flex items-center">
159
- <i class="fas fa-search text-green-600 ml-2"></i> نتائج التحليل والمقارنة
160
- </h2>
161
- <!-- هنا يتم عرض الأخطاء والاختلافات فقط -->
162
- <div id="errorsList" class="space-y-3 mb-6"></div>
163
- <div class="result-section split-view">
164
- <!-- عرض النص المصدر بعد التعليم مع تقسيمه إلى أسطر -->
165
- <div>
166
- <h4 class="text-lg font-bold text-gray-700 mb-3 flex items-center">
167
- <i class="fas fa-file-alt text-blue-600 ml-2"></i> النص المصدر (مع التعليم)
168
- </h4>
169
- <div id="sourceTextReview" class="bg-blue-50 rounded-xl p-6 min-h-[200px] border-2 border-blue-100 text-comparison"></div>
170
- </div>
171
- <!-- عرض النص الهدف بعد التعليم مع تقسيمه إلى أسطر -->
172
- <div>
173
- <h4 class="text-lg font-bold text-gray-700 mb-3 flex items-center">
174
- <i class="fas fa-file-alt text-purple-600 ml-2"></i> النص الهدف (مع التعليم)
175
- </h4>
176
- <div id="targetTextReview" class="bg-gray-50 rounded-xl p-6 min-h-[200px] border-2 border-gray-200 text-comparison"></div>
177
- </div>
178
- </div>
179
  </div>
 
 
 
 
 
 
 
 
180
 
181
- <!-- صندوق شرح الاختلافات -->
182
- <div id="explanationBox" class="bg-white rounded-2xl shadow-lg p-8 border border-gray-100 hover:shadow-xl transition-all animate-scale mb-8 hidden card-hover">
183
- <h2 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-3 flex items-center">
184
- <i class="fas fa-info-circle text-green-600 ml-2"></i> شرح الاختلافات
185
- </h2>
186
- <div id="explanationText" class="text-lg text-gray-700"></div>
187
  </div>
188
- </main>
 
 
 
 
189
  </div>
190
 
191
- <!-- JavaScript -->
192
  <script>
193
- // API URL ومفتاح API (تم التحديث لاستخدام DeepSeek API)
194
- const API_URL = 'https://api.deepseek.com/chat/completions';
195
- const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
196
-
197
- /*
198
- الـ prompt المحسن:
199
- أنت خبير في تحليل النصوص ومقارنتها. قم بمقارنة النص المصدر والنص الهدف بدقة عالية واكتشف الاختلافات التالية فقط:
200
-
201
- 1. اختلافات الأرقام: ضع الرقم المختلف بين علامتي "<" و ">"
202
- 2. النصوص المفقودة: إذا كان هناك نص موجود في المصدر ومفقود في الهدف، ضع النص المفقود بين "__" و "__"
203
- 3. اختلافات المعنى: إذا كانت هناك كلمة أو عبارة تختلف في المعنى، ضعها بين [MEANING] و [/MEANING]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
- إذا كانت النصوص متطابقة تماماً، اكتب [MATCH] فقط.
206
 
207
- اعتبر النص المصدر هو المرجع الأساسي للمقارنة.
 
 
 
208
 
209
- النص المصدر:
210
- {source}
 
211
 
212
- النص الهدف:
213
- {target}
 
 
214
 
215
- أخرج فقط الاختلافات المحددة بالعلامات المذكورة أعلاه، دون أي شرح إضافي.
216
- */
217
- const ANALYSIS_PROMPT = `أنت خبير في تحليل النصوص ومقارنتها. قم بمقارنة النص المصدر والنص الهدف بدقة عالية واكتشف الاختلافات التالية فقط:
218
-
219
- 1. اختلافات الأرقام: ضع الرقم المختلف بين علامتي "<" و ">"
220
- 2. النصوص المفقودة: إذا كان هناك نص موجود في المصدر ومفقود في الهدف، ضع النص المفقود بين "__" و "__"
221
- 3. اختلافات المعنى: إذا كانت هناك كلمة أو عبارة تختلف في المعنى، ضعها بين [MEANING] و [/MEANING]
222
-
223
- إذا كانت النصوص متطابقة تماماً، اكتب [MATCH] فقط.
224
-
225
- اعتبر النص المصدر هو المرجع الأساسي للمقارنة.
226
-
227
- النص المصدر:
228
- {source}
229
-
230
- النص الهدف:
231
- {target}
232
-
233
- أخرج فقط الاختلافات المحددة بالعلامات المذكورة أعلاه، دون أي شرح إضافي.`;
234
-
235
- // دالة لحساب عدد الكلمات
236
- function countWords(text) {
237
- return text.trim().split(/\s+/).filter(word => word !== "").length;
238
- }
239
-
240
- // دالة لمساعدة في الهروب من أحرف regex الخاصة
241
- function escapeRegExp(string) {
242
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
243
- }
244
-
245
- // دالة تطبيق التمييز على النص باستخدام العلامات في نتيجة التحليل
246
- function applyHighlights(originalText, analysisOutput) {
247
- let highlightedText = originalText;
248
- // تمييز اختلافات الأرقام
249
- const numberRegex = /<([^<>]+)>/g;
250
- let match;
251
- while ((match = numberRegex.exec(analysisOutput)) !== null) {
252
- const phrase = match[1].trim();
253
- if (phrase) {
254
- const replacement = `<span class="highlight-number">${phrase}</span>`;
255
- const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
256
- highlightedText = highlightedText.replace(phraseRegex, replacement);
257
- }
258
- }
259
- // تمييز النصوص الناقصة
260
- const missingRegex = /__(.*?)__/g;
261
- while ((match = missingRegex.exec(analysisOutput)) !== null) {
262
- const phrase = match[1].trim();
263
- if (phrase) {
264
- const replacement = `<span class="highlight-missing">__${phrase}__</span>`;
265
- const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
266
- highlightedText = highlightedText.replace(phraseRegex, replacement);
267
- }
268
- }
269
- // تمييز اختلاف المعنى
270
- const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
271
- while ((match = meaningRegex.exec(analysisOutput)) !== null) {
272
- const phrase = match[1].trim();
273
- if (phrase) {
274
- const replacement = `<span class="highlight-meaning">${phrase}</span>`;
275
- const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
276
- highlightedText = highlightedText.replace(phraseRegex, replacement);
277
  }
 
 
 
 
 
 
 
 
 
 
 
278
  }
279
- return highlightedText;
280
- }
281
-
282
- // دالة تقسيم النص إلى أسطر مع عرض رقم السطر
283
- function splitIntoLines(text) {
284
- const lines = text.split('\n');
285
- let html = "";
286
- lines.forEach((line, index) => {
287
- html += `<div class="line-item"><span class="line-number">${index + 1}:</span> <span class="line-text">${line}</span></div>`;
288
- });
289
- return html;
290
  }
291
-
292
- // دالة للحصول على رقم السطر لظهور عبارة معينة في النص
293
- function getLineNumber(text, substring) {
294
- const index = text.indexOf(substring);
295
- if (index === -1) return "غير محدد";
296
- return text.substring(0, index).split("\n").length;
297
- }
298
-
299
- // دالة توليد شرح الاختلافات
300
- function generateExplanation(sourceText, analysisOutput) {
301
- let explanation = "";
302
- // شرح النصوص الناقصة
303
- const missingRegex = /__(.*?)__/g;
304
- let match;
305
- while ((match = missingRegex.exec(analysisOutput)) !== null) {
306
- const phrase = match[1].trim();
307
- if (phrase) {
308
- const lineNum = getLineNumber(sourceText, phrase);
309
- explanation += `<p>في السطر ${lineNum}: النص "${phrase}" مفقود في النص الهدف.</p>`;
310
- }
311
- }
312
- // شرح اختلاف الأرقام
313
- const numberRegex = /<([^<>]+)>/g;
314
- while ((match = numberRegex.exec(analysisOutput)) !== null) {
315
- const phrase = match[1].trim();
316
- if (phrase) {
317
- const lineNum = getLineNumber(sourceText, phrase);
318
- explanation += `<p>في السطر ${lineNum}: اختلاف في الرقم "${phrase}".</p>`;
319
  }
320
- }
321
- // شرح اختلاف المعنى
322
- const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
323
- while ((match = meaningRegex.exec(analysisOutput)) !== null) {
324
- const phrase = match[1].trim();
325
- if (phrase) {
326
- const lineNum = getLineNumber(sourceText, phrase);
327
- explanation += `<p>في السطر ${lineNum}: اختلاف في المعنى: "${phrase}".</p>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  }
 
 
 
 
329
  }
330
- if (explanation === "") {
331
- explanation = `<p>[MATCH] النصوص متطابقة تماماً.</p>`;
332
- }
333
- return explanation;
334
- }
335
-
336
- // دالة موحدة لمعالجة الملفات (PDF و DOCX)
337
- async function processFile(file) {
338
- let text = "";
339
- if (file.type === 'application/pdf') {
340
- const form = new FormData();
341
- form.append('image', file);
342
- const response = await fetch('https://demo.api4ai.cloud/ocr/v1/results', {
343
- method: 'POST',
344
- body: form,
345
- headers: { 'A4A-CLIENT-APP-ID': 'sample' }
346
- });
347
- const data = await response.json();
348
- text = data.results[0].entities[0].objects[0].entities[0].text;
349
- } else if (file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
350
- const arrayBuffer = await file.arrayBuffer();
351
- const result = await mammoth.extractRawText({ arrayBuffer });
352
- text = result.value;
353
- } else {
354
- throw new Error('نوع الملف غير مدعوم');
355
- }
356
- return text;
357
  }
358
-
359
- // رفع ملف السورس
360
- document.getElementById('sourceFile')?.addEventListener('change', async (event) => {
361
- const file = event.target.files[0];
362
- if (!file) return;
363
- document.getElementById('processStatus').classList.remove('hidden');
364
  try {
365
- const text = await processFile(file);
366
- document.getElementById('sourceText').value = text;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  } catch (error) {
368
- console.error('Error processing source file:', error);
369
- addError('خطأ في معالجة ملف السورس');
370
- } finally {
371
- document.getElementById('processStatus').classList.add('hidden');
372
  }
373
- });
374
-
375
- // رفع ملف التارجت
376
- document.getElementById('targetFile')?.addEventListener('change', async (event) => {
377
- const file = event.target.files[0];
378
- if (!file) return;
379
- document.getElementById('processStatus').classList.remove('hidden');
380
  try {
381
- const text = await processFile(file);
382
- document.getElementById('targetText').value = text;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  } catch (error) {
384
- console.error('Error processing target file:', error);
385
- addError('خطأ في معالجة ملف التارجت');
386
- } finally {
387
- document.getElementById('processStatus').classList.add('hidden');
388
  }
389
- });
390
-
391
- // دالة عرض الأخطاء أو الرسائل
392
- function addError(message, type = 'error') {
393
- const errorsList = document.getElementById('errorsList');
394
- if (!errorsList) return;
395
- const errorDiv = document.createElement('div');
396
- errorDiv.className = `p-4 rounded-xl ${type === 'error' ? 'bg-red-50 text-red-700' : 'bg-yellow-50 text-yellow-700'}`;
397
- errorDiv.innerHTML = `<div class="flex items-center">
398
- <i class="fas fa-${type === 'error' ? 'exclamation-circle' : 'info-circle'} ml-2 text-${type === 'error' ? 'red' : 'yellow'}-500"></i>
399
- <span>${message}</span>
400
- </div>`;
401
- errorsList.appendChild(errorDiv);
402
  }
403
-
404
- // عند الضغط على زر التحليل
405
- document.getElementById('submitBtn').addEventListener('click', async () => {
406
- const sourceText = document.getElementById('sourceText').value;
407
- const targetText = document.getElementById('targetText').value;
 
 
 
 
 
408
 
409
- // مسح الرسائل السابقة وإظهار قسم النتائج
410
- document.getElementById('errorsList').innerHTML = '';
411
- document.getElementById('resultSection').classList.remove('hidden');
412
- document.getElementById('explanationBox').classList.remove('hidden');
413
 
414
- if (!sourceText || !targetText) {
415
- addError('يرجى إدخال كلا النصين المصدر والهدف');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  return;
417
  }
418
 
419
- // مقارنة عدد الكل��ات (تنبيه اختياري)
420
- const sourceWordCount = countWords(sourceText);
421
- const targetWordCount = countWords(targetText);
422
- if (sourceWordCount !== targetWordCount) {
423
- addError(`عدد كلمات النص المصدر (${sourceWordCount}) يختلف عن النص الهدف (${targetWordCount})`, 'warning');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  }
425
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  try {
427
- // عرض مؤشر التقدم أثناء انتظار الرد
428
- const progressDiv = document.createElement('div');
429
- progressDiv.className = "bg-blue-100 p-4 rounded-xl mb-4";
430
- progressDiv.innerHTML = `<div class="flex items-center">
431
- <div class="animate-spin h-6 w-6 border-4 border-blue-600 rounded-full border-t-transparent ml-3"></div>
432
- <span>جارٍ التحليل...</span>
433
- </div>`;
434
- document.getElementById('errorsList').appendChild(progressDiv);
435
-
436
- // بناء prompt باستخدام النصين المُدخلين
437
- const prompt = ANALYSIS_PROMPT
438
- .replace("{source}", sourceText)
439
- .replace("{target}", targetText);
440
 
441
- // استخدام DeepSeek API
442
- const payload = {
443
- model: "deepseek-chat",
444
- messages: [
445
- { role: "system", content: "أنت خبير في تحليل النصوص ومقارنتها بدقة عالية." },
446
- { role: "user", content: prompt }
447
- ],
448
- temperature: 0.3,
449
- max_tokens: 2048,
450
- stream: false
451
- };
452
-
453
- // استدعاء API التحليل
454
- const response = await fetch(API_URL, {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  method: 'POST',
456
  headers: {
457
- 'Authorization': 'Bearer ' + API_KEY,
458
- 'Content-Type': 'application/json'
459
  },
460
- body: JSON.stringify(payload)
461
  });
462
 
463
- if (!response.ok) {
464
- throw new Error('حدث خطأ بالشبكة: ' + response.statusText);
465
  }
466
 
467
- const data = await response.json();
468
- const analysisOutput = data.choices[0].message.content.trim();
469
 
470
- progressDiv.remove();
471
-
472
- if (analysisOutput.includes('[MATCH]')) {
473
- const checkDiv = document.createElement('div');
474
- checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
475
- checkDiv.innerHTML = `<i class="fas fa-check-circle ml-2 text-lg"></i> <span class="text-lg">النصوص متطابقة تماماً</span>`;
476
- document.getElementById('errorsList').appendChild(checkDiv);
477
- document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceText);
478
- document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
479
- document.getElementById('explanationText').innerHTML = `<p>[MATCH] لا توجد اختلافات.</p>`;
480
- } else {
481
- const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
482
- const targetHighlighted = applyHighlights(targetText, analysisOutput);
483
- // تقسيم النصوص المُظّلّة إلى أسطر مع رقم السطر
484
- document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
485
- document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
486
-
487
- // توليد شرح الاختلافات
488
- const explanationHTML = generateExplanation(sourceText, analysisOutput);
489
- document.getElementById('explanationText').innerHTML = explanationHTML;
490
-
491
- // عرض ملخص الاختلافات إن وُجدت
492
- const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
493
- const missingDiffCount = (analysisOutput.match(/__(.*?)__/g) || []).length;
494
- const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
495
- if (numDiffCount > 0 || missingDiffCount > 0 || meaningDiffCount > 0) {
496
- const summaryDiv = document.createElement('div');
497
- summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
498
- let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc mr-6 space-y-1">';
499
- if (numDiffCount > 0) summaryText += `<li>اختلاف في الأرقام: ${numDiffCount}</li>`;
500
- if (missingDiffCount > 0) summaryText += `<li>نصوص مفقودة: ${missingDiffCount}</li>`;
501
- if (meaningDiffCount > 0) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
502
- summaryText += '</ul>';
503
- summaryDiv.innerHTML = summaryText;
504
- document.getElementById('errorsList').appendChild(summaryDiv);
505
- }
506
  }
507
-
508
  } catch (error) {
509
- document.getElementById('errorsList').innerHTML = '';
510
- addError(`خطأ في التحليل: ${error.message}`);
511
  }
512
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  </script>
514
  </body>
515
  </html>
 
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/bootstrap@5.2.3/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>