stat2025 commited on
Commit
a2d435c
·
verified ·
1 Parent(s): ed1606f

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +273 -172
index.html CHANGED
@@ -1,193 +1,294 @@
1
- <script>
2
- const filesInput = document.getElementById("files");
3
- const mergeBtn = document.getElementById("mergeBtn");
4
- const statusDiv = document.getElementById("status");
5
- const fileListDiv = document.getElementById("fileList");
6
- const outputNameInput = document.getElementById("outputName");
7
-
8
- // نخزن جميع الملفات المختارة (من عدة مرات اختيار)
9
- let selectedFiles = [];
10
-
11
- function setStatus(msg, type = "") {
12
- statusDiv.textContent = msg;
13
- statusDiv.className = "status" + (type ? " " + type : "");
14
- if (!msg) statusDiv.className = "status";
15
- }
16
-
17
- function renderFileList(files) {
18
- if (!files.length) {
19
- fileListDiv.classList.add("hidden");
20
- fileListDiv.innerHTML = "";
21
- return;
22
- }
23
- fileListDiv.classList.remove("hidden");
24
- fileListDiv.innerHTML = `
25
- <div class="file-list-header">
26
- <span>الملفات المختارة: ${files.length}</span>
27
- <span class="file-note">الدمج حسب الترتيب الأبجدي لاسم الملف.</span>
28
  </div>
29
- <ul class="file-list-ul">
30
- ${files
31
- .map(
32
- (f, i) => `
33
- <li>
34
- <span class="index">${i + 1}</span>
35
- <span class="name">${f.name}</span>
36
- <span class="size">${(f.size / 1024).toFixed(1)} كيلوبايت</span>
37
- </li>`
38
- )
39
- .join("")}
40
- </ul>
41
- `;
42
- }
43
-
44
- function detectMode(files) {
45
- const allImages = files.every(f => f.type.startsWith("image/"));
46
- const allPDFs = files.every(
47
- f =>
48
- f.type === "application/pdf" ||
49
- f.name.toLowerCase().endsWith(".pdf")
50
- );
51
- if (allImages) return "images";
52
- if (allPDFs) return "pdfs";
53
- return null;
54
- }
55
-
56
- function downloadPdf(bytes, filename) {
57
- const blob = new Blob([bytes], { type: "application/pdf" });
58
- const url = URL.createObjectURL(blob);
59
- const a = document.createElement("a");
60
- a.href = url;
61
- a.download = filename;
62
- document.body.appendChild(a);
63
- a.click();
64
- a.remove();
65
- URL.revokeObjectURL(url);
66
- }
67
-
68
- // السماح بالإضافة على دفعات (جوال / كمبيوتر)
69
- filesInput.addEventListener("change", () => {
70
- const newFiles = Array.from(filesInput.files || []);
71
-
72
- if (!newFiles.length) return;
73
-
74
- // دمج بدون تكرار (حسب الاسم + الحجم + تاريخ التعديل)
75
- const map = new Map();
76
- [...selectedFiles, ...newFiles].forEach(f => {
77
- const key = `${f.name}|${f.size}|${f.lastModified}`;
78
- if (!map.has(key)) map.set(key, f);
79
- });
 
 
 
 
 
80
 
81
- selectedFiles = Array.from(map.values()).sort((a, b) =>
82
- a.name.localeCompare(b.name, undefined, { numeric: true })
83
- );
 
 
 
 
 
 
 
84
 
85
- renderFileList(selectedFiles);
86
- setStatus("");
 
 
 
 
87
 
88
- // مسح قيمة input حتى نقدر نختار نفس الملف مرة ثانية لو حبينا
89
- filesInput.value = "";
90
- });
 
 
91
 
92
- mergeBtn.addEventListener("click", async () => {
93
- let files = [...selectedFiles];
 
 
 
 
94
 
95
- if (!files.length) {
96
- setStatus("الرجاء اختيار الملفات أولاً.", "error");
97
- return;
 
 
 
 
98
  }
99
 
100
- files.sort((a, b) =>
101
- a.name.localeCompare(b.name, undefined, { numeric: true })
102
- );
103
- renderFileList(files);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- const mode = detectMode(files);
106
- if (!mode) {
107
- setStatus(
108
- "يجب أن تكون جميع الملفات صورًا فقط أو جميعها PDF فقط في العملية الواحدة.",
109
- "error"
 
110
  );
111
- return;
 
 
112
  }
113
 
114
- try {
115
- setStatus("جاري معالجة الملفات...", "loading");
116
- mergeBtn.disabled = true;
117
- mergeBtn.classList.add("disabled");
118
-
119
- if (mode === "images") {
120
- const pdfDoc = await PDFLib.PDFDocument.create();
121
-
122
- for (const file of files) {
123
- const bytes = await file.arrayBuffer();
124
- const lower = file.name.toLowerCase();
125
- let image;
126
-
127
- if (
128
- file.type === "image/jpeg" ||
129
- file.type === "image/jpg" ||
130
- lower.endsWith(".jpg") ||
131
- lower.endsWith(".jpeg")
132
- ) {
133
- image = await pdfDoc.embedJpg(bytes);
134
- } else {
135
- image = await pdfDoc.embedPng(bytes);
136
- }
137
 
138
- const imgWidth = image.width;
139
- const imgHeight = image.height;
140
- const pageWidth = 595.28; // A4
141
- const pageHeight = 841.89; // A4
142
 
143
- const page = pdfDoc.addPage([pageWidth, pageHeight]);
144
- const scale = Math.min(pageWidth / imgWidth, pageHeight / imgHeight);
145
- const drawWidth = imgWidth * scale;
146
- const drawHeight = imgHeight * scale;
147
- const x = (pageWidth - drawWidth) / 2;
148
- const y = (pageHeight - drawHeight) / 2;
149
 
150
- page.drawImage(image, { x, y, width: drawWidth, height: drawHeight });
151
- }
 
 
 
 
 
 
 
 
 
152
 
153
- const pdfBytes = await pdfDoc.save();
154
- const outName =
155
- (outputNameInput.value || "merged-images.pdf").trim() ||
156
- "merged-images.pdf";
157
- downloadPdf(pdfBytes, outName);
158
- setStatus("تم إنشاء ملف PDF من الصور بنجاح.", "ok");
159
  }
160
 
161
- if (mode === "pdfs") {
162
- const pdfDoc = await PDFLib.PDFDocument.create();
163
-
164
- for (const file of files) {
165
- const bytes = await file.arrayBuffer();
166
- const donorPdf = await PDFLib.PDFDocument.load(bytes);
167
- const pages = await pdfDoc.copyPages(
168
- donorPdf,
169
- donorPdf.getPageIndices()
170
- );
171
- pages.forEach(p => pdfDoc.addPage(p));
172
- }
173
 
174
- const pdfBytes = await pdfDoc.save();
175
- const outName =
176
- (outputNameInput.value || "merged-pdfs.pdf").trim() ||
177
- "merged-pdfs.pdf";
178
- downloadPdf(pdfBytes, outName);
179
- setStatus("تم دمج ملفات PDF بنجاح.", "ok");
 
180
  }
181
 
182
- // إعادة التهيئة بعد الانتهاء
183
- selectedFiles = [];
184
- renderFileList([]);
185
- } catch (err) {
186
- console.error(err);
187
- setStatus("حدث خطأ أثناء المعالجة. تأكد من الملفات وحاول مرة أخرى.", "error");
188
- } finally {
189
- mergeBtn.disabled = false;
190
- mergeBtn.classList.remove("disabled");
191
- }
192
- });
193
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ar" dir="rtl">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>أداة دمج ملفات المجموعة | PDF واحد بسهولة</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <link rel="stylesheet" href="style.css" />
8
+ <script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
9
+ </head>
10
+ <body>
11
+ <div class="page">
12
+
13
+ <!-- الهيدر العلوي -->
14
+ <header class="topbar">
15
+ <div class="top-left">
16
+ <div class="logo-mark">PDF</div>
17
+ <div class="brand-text">
18
+ <div class="brand-title">أداة دمج ملفات المجموعة</div>
19
+ <div class="brand-sub">دمج احترافي لصور وملفات PDF في ملف واحد</div>
20
+ </div>
 
 
 
 
 
 
 
21
  </div>
22
+ <div class="top-right">
23
+ <span class="credit">تصميم وإعداد الدعم الفني: نوف الناصر</span>
24
+ </div>
25
+ </header>
26
+
27
+ <!-- المحتوى الرئيسي -->
28
+ <main class="main">
29
+
30
+ <!-- تعريف بسيط -->
31
+ <section class="hero">
32
+ <h1>ادمج ملفاتك بسهولة في PDF واحد منسّق.</h1>
33
+ <p>
34
+ اختر مجموعة من الصور أو ملفات PDF، وسيتم إنشاء ملف PDF واحد مرتب حسب أسماء الملفات،
35
+ للاستخدام الداخلي ضمن مجموعتك.
36
+ </p>
37
+ </section>
38
+
39
+ <!-- خطوات مختصرة -->
40
+ <section class="steps">
41
+ <div class="step">
42
+ <span class="step-number">1</span>
43
+ <span class="step-text">اضغط لاختيار الملفات (يمكن الإضافة على دفعات).</span>
44
+ </div>
45
+ <div class="step">
46
+ <span class="step-number">2</span>
47
+ <span class="step-text">اختر صورًا فقط أو ملفات PDF فقط في العملية نفسها.</span>
48
+ </div>
49
+ <div class="step">
50
+ <span class="step-number">3</span>
51
+ <span class="step-text">احصل على ملف PDF واحد جاهز للتحميل والمشاركة.</span>
52
+ </div>
53
+ </section>
54
+
55
+ <!-- الكارد الرئيسي -->
56
+ <section class="card main-card">
57
+
58
+ <!-- اختيار الملفات -->
59
+ <div class="card-row">
60
+ <h2 class="card-title">اختر الملفات المراد دمجها</h2>
61
+ <p class="hint">
62
+ يدعم:
63
+ <strong>دمج عدة ملفات PDF</strong> في ملف واحد،
64
+ أو
65
+ <strong>تحويل عدة صور (JPG / PNG)</strong> إلى ملف PDF واحد.
66
+ يُمنع خلط الصور وملفات PDF في نفس العملية.
67
+ </p>
68
+
69
+ <label class="file-picker">
70
+ <span class="file-picker-icon">📂</span>
71
+ <span class="file-picker-text">اضغط هنا لاختيار الملفات من جهازك</span>
72
+ <input id="files" type="file" multiple accept=".pdf,image/*" />
73
+ </label>
74
+ </div>
75
+
76
+ <!-- قائمة الملفات المختارة -->
77
+ <div id="fileList" class="file-list hidden"></div>
78
 
79
+ <!-- اسم الملف الناتج -->
80
+ <div class="card-row inline">
81
+ <label for="outputName" class="card-label">اسم ملف الإخراج (اختياري)</label>
82
+ <input
83
+ id="outputName"
84
+ type="text"
85
+ class="output-input"
86
+ placeholder="مثال: group-merged.pdf"
87
+ />
88
+ </div>
89
 
90
+ <!-- زر الدمج -->
91
+ <div class="actions">
92
+ <button id="mergeBtn" class="btn-main">
93
+ دمج وإنشاء ملف PDF واحد
94
+ </button>
95
+ </div>
96
 
97
+ <!-- حالة العملية -->
98
+ <div id="status" class="status"></div>
99
+ </section>
100
+ </main>
101
+ </div>
102
 
103
+ <script>
104
+ const filesInput = document.getElementById("files");
105
+ const mergeBtn = document.getElementById("mergeBtn");
106
+ const statusDiv = document.getElementById("status");
107
+ const fileListDiv = document.getElementById("fileList");
108
+ const outputNameInput = document.getElementById("outputName");
109
 
110
+ // نخزن جميع الملفات المختارة من عدة مرات اختيار
111
+ let selectedFiles = [];
112
+
113
+ function setStatus(msg, type = "") {
114
+ statusDiv.textContent = msg;
115
+ statusDiv.className = "status" + (type ? " " + type : "");
116
+ if (!msg) statusDiv.className = "status";
117
  }
118
 
119
+ function renderFileList(files) {
120
+ if (!files.length) {
121
+ fileListDiv.classList.add("hidden");
122
+ fileListDiv.innerHTML = "";
123
+ return;
124
+ }
125
+ fileListDiv.classList.remove("hidden");
126
+ fileListDiv.innerHTML = `
127
+ <div class="file-list-header">
128
+ <span>الملفات المختارة: ${files.length}</span>
129
+ <span class="file-note">سيتم الدمج حسب الترتيب الأبجدي لاسم الملف.</span>
130
+ </div>
131
+ <ul class="file-list-ul">
132
+ ${files
133
+ .map(
134
+ (f, i) => `
135
+ <li>
136
+ <span class="index">${i + 1}</span>
137
+ <span class="name">${f.name}</span>
138
+ <span class="size">${(f.size / 1024).toFixed(1)} كيلوبايت</span>
139
+ </li>`
140
+ )
141
+ .join("")}
142
+ </ul>
143
+ `;
144
+ }
145
 
146
+ function detectMode(files) {
147
+ const allImages = files.every(f => f.type.startsWith("image/"));
148
+ const allPDFs = files.every(
149
+ f =>
150
+ f.type === "application/pdf" ||
151
+ f.name.toLowerCase().endsWith(".pdf")
152
  );
153
+ if (allImages) return "images";
154
+ if (allPDFs) return "pdfs";
155
+ return null;
156
  }
157
 
158
+ function downloadPdf(bytes, filename) {
159
+ const blob = new Blob([bytes], { type: "application/pdf" });
160
+ const url = URL.createObjectURL(blob);
161
+ const a = document.createElement("a");
162
+ a.href = url;
163
+ a.download = filename;
164
+ document.body.appendChild(a);
165
+ a.click();
166
+ a.remove();
167
+ URL.revokeObjectURL(url);
168
+ }
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
+ // السماح بالإضافة على دفعات (مهم للموبايل)
171
+ filesInput.addEventListener("change", () => {
172
+ const newFiles = Array.from(filesInput.files || []);
173
+ if (!newFiles.length) return;
174
 
175
+ // دمج بدون تكرار (اسم + حجم + تاريخ تعديل)
176
+ const map = new Map();
177
+ [...selectedFiles, ...newFiles].forEach(f => {
178
+ const key = `${f.name}|${f.size}|${f.lastModified}`;
179
+ if (!map.has(key)) map.set(key, f);
180
+ });
181
 
182
+ selectedFiles = Array.from(map.values()).sort((a, b) =>
183
+ a.name.localeCompare(b.name, undefined, { numeric: true })
184
+ );
185
+
186
+ renderFileList(selectedFiles);
187
+ setStatus("");
188
+ filesInput.value = ""; // للسماح بالاختيار مرة أخرى
189
+ });
190
+
191
+ mergeBtn.addEventListener("click", async () => {
192
+ let files = [...selectedFiles];
193
 
194
+ if (!files.length) {
195
+ setStatus("الرجاء اختيار الملفات أولاً.", "error");
196
+ return;
 
 
 
197
  }
198
 
199
+ files.sort((a, b) =>
200
+ a.name.localeCompare(b.name, undefined, { numeric: true })
201
+ );
202
+ renderFileList(files);
 
 
 
 
 
 
 
 
203
 
204
+ const mode = detectMode(files);
205
+ if (!mode) {
206
+ setStatus(
207
+ "يجب أن تكون جميع الملفات صورًا فقط أو جميعها PDF فقط في العملية الواحدة.",
208
+ "error"
209
+ );
210
+ return;
211
  }
212
 
213
+ try {
214
+ setStatus("جاري معالجة الملفات...", "loading");
215
+ mergeBtn.disabled = true;
216
+ mergeBtn.classList.add("disabled");
217
+
218
+ if (mode === "images") {
219
+ const pdfDoc = await PDFLib.PDFDocument.create();
220
+
221
+ for (const file of files) {
222
+ const bytes = await file.arrayBuffer();
223
+ const lower = file.name.toLowerCase();
224
+ let image;
225
+
226
+ if (
227
+ file.type === "image/jpeg" ||
228
+ file.type === "image/jpg" ||
229
+ lower.endsWith(".jpg") ||
230
+ lower.endsWith(".jpeg")
231
+ ) {
232
+ image = await pdfDoc.embedJpg(bytes);
233
+ } else {
234
+ image = await pdfDoc.embedPng(bytes);
235
+ }
236
+
237
+ const imgWidth = image.width;
238
+ const imgHeight = image.height;
239
+ const pageWidth = 595.28; // A4
240
+ const pageHeight = 841.89; // A4
241
+
242
+ const page = pdfDoc.addPage([pageWidth, pageHeight]);
243
+ const scale = Math.min(pageWidth / imgWidth, pageHeight / imgHeight);
244
+ const drawWidth = imgWidth * scale;
245
+ const drawHeight = imgHeight * scale;
246
+ const x = (pageWidth - drawWidth) / 2;
247
+ const y = (pageHeight - drawHeight) / 2;
248
+
249
+ page.drawImage(image, { x, y, width: drawWidth, height: drawHeight });
250
+ }
251
+
252
+ const pdfBytes = await pdfDoc.save();
253
+ const outName =
254
+ (outputNameInput.value || "merged-images.pdf").trim() ||
255
+ "merged-images.pdf";
256
+ downloadPdf(pdfBytes, outName);
257
+ setStatus("تم إنشاء ملف PDF من الصور بنجاح.", "ok");
258
+ }
259
+
260
+ if (mode === "pdfs") {
261
+ const pdfDoc = await PDFLib.PDFDocument.create();
262
+
263
+ for (const file of files) {
264
+ const bytes = await file.arrayBuffer();
265
+ const donorPdf = await PDFLib.PDFDocument.load(bytes);
266
+ const pages = await pdfDoc.copyPages(
267
+ donorPdf,
268
+ donorPdf.getPageIndices()
269
+ );
270
+ pages.forEach(p => pdfDoc.addPage(p));
271
+ }
272
+
273
+ const pdfBytes = await pdfDoc.save();
274
+ const outName =
275
+ (outputNameInput.value || "merged-pdfs.pdf").trim() ||
276
+ "merged-pdfs.pdf";
277
+ downloadPdf(pdfBytes, outName);
278
+ setStatus("تم دمج ملفات PDF بنجاح.", "ok");
279
+ }
280
+
281
+ // إعادة التهيئة بعد الانتهاء
282
+ selectedFiles = [];
283
+ renderFileList([]);
284
+ } catch (err) {
285
+ console.error(err);
286
+ setStatus("حدث خطأ أثناء المعالجة. تأكد من الملفات وحاول مرة أخرى.", "error");
287
+ } finally {
288
+ mergeBtn.disabled = false;
289
+ mergeBtn.classList.remove("disabled");
290
+ }
291
+ });
292
+ </script>
293
+ </body>
294
+ </html>