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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +172 -255
index.html CHANGED
@@ -1,276 +1,193 @@
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
- <div class="card-row">
59
- <h2 class="card-title">اختر الملفات المراد دمجها</h2>
60
- <p class="hint">
61
- يدعم:
62
- <strong>عدة ملفات PDF</strong> ➜ ملف PDF واحد،
63
- أو
64
- <strong>عدة صور (JPG / PNG)</strong> ملف PDF واحد.
65
- يُفضّل عدم خلط النوعين في نفس العملية.
66
- </p>
67
-
68
- <label class="file-picker">
69
- <span class="file-picker-icon">📂</span>
70
- <span class="file-picker-text">اضغط هنا لاختيار الملفات من جهازك</span>
71
- <input id="files" type="file" multiple accept=".pdf,image/*" />
72
- </label>
73
- </div>
74
-
75
- <!-- قائمة الملفات المختارة -->
76
- <div id="fileList" class="file-list hidden"></div>
77
 
78
- <!-- اسم الملف الناتج -->
79
- <div class="card-row inline">
80
- <label for="outputName" class="card-label">اسم ملف الإخراج (اختياري)</label>
81
- <input
82
- id="outputName"
83
- type="text"
84
- class="output-input"
85
- placeholder="مثال: group-report.pdf"
86
- />
87
- </div>
88
 
89
- <!-- زر الدمج -->
90
- <div class="actions">
91
- <button id="mergeBtn" class="btn-main">
92
- دمج وإنشاء ملف PDF واحد
93
- </button>
94
- </div>
95
 
96
- <!-- حالة العملية -->
97
- <div id="status" class="status"></div>
98
- </section>
99
- </main>
100
- </div>
101
 
102
- <script>
103
- const filesInput = document.getElementById("files");
104
- const mergeBtn = document.getElementById("mergeBtn");
105
- const statusDiv = document.getElementById("status");
106
- const fileListDiv = document.getElementById("fileList");
107
- const outputNameInput = document.getElementById("outputName");
108
 
109
- function setStatus(msg, type = "") {
110
- statusDiv.textContent = msg;
111
- statusDiv.className = "status" + (type ? " " + type : "");
112
- if (!msg) statusDiv.className = "status";
113
  }
114
 
115
- function renderFileList(files) {
116
- if (!files.length) {
117
- fileListDiv.classList.add("hidden");
118
- fileListDiv.innerHTML = "";
119
- return;
120
- }
121
- fileListDiv.classList.remove("hidden");
122
- fileListDiv.innerHTML = `
123
- <div class="file-list-header">
124
- <span>الملفات المختارة: ${files.length}</span>
125
- <span class="file-note">الدمج حسب الترتيب الأبجدي لاسم الملف.</span>
126
- </div>
127
- <ul class="file-list-ul">
128
- ${files
129
- .map(
130
- (f, i) => `
131
- <li>
132
- <span class="index">${i + 1}</span>
133
- <span class="name">${f.name}</span>
134
- <span class="size">${(f.size / 1024).toFixed(1)} كيلوبايت</span>
135
- </li>`
136
- )
137
- .join("")}
138
- </ul>
139
- `;
140
- }
141
 
142
- function detectMode(files) {
143
- const allImages = files.every(f => f.type.startsWith("image/"));
144
- const allPDFs = files.every(
145
- f =>
146
- f.type === "application/pdf" ||
147
- f.name.toLowerCase().endsWith(".pdf")
148
  );
149
- if (allImages) return "images";
150
- if (allPDFs) return "pdfs";
151
- return null;
152
  }
153
 
154
- function downloadPdf(bytes, filename) {
155
- const blob = new Blob([bytes], { type: "application/pdf" });
156
- const url = URL.createObjectURL(blob);
157
- const a = document.createElement("a");
158
- a.href = url;
159
- a.download = filename;
160
- document.body.appendChild(a);
161
- a.click();
162
- a.remove();
163
- URL.revokeObjectURL(url);
164
- }
165
-
166
- filesInput.addEventListener("change", () => {
167
- const files = Array.from(filesInput.files || []).sort((a, b) =>
168
- a.name.localeCompare(b.name, undefined, { numeric: true })
169
- );
170
- renderFileList(files);
171
- setStatus("");
172
- });
173
-
174
- mergeBtn.addEventListener("click", async () => {
175
- let files = Array.from(filesInput.files || []);
176
- if (!files.length) {
177
- setStatus("الرجاء اختيار الملفات أولاً.", "error");
178
- return;
179
- }
180
-
181
- files = files.sort((a, b) =>
182
- a.name.localeCompare(b.name, undefined, { numeric: true })
183
- );
184
- renderFileList(files);
185
-
186
- const mode = detectMode(files);
187
- if (!mode) {
188
- setStatus(
189
- "يجب أن تكون جميع الملفات صورًا فقط أو جميعها PDF فقط في العملية الواحدة.",
190
- "error"
191
- );
192
- return;
193
- }
194
-
195
- try {
196
- setStatus("جاري معالجة الملفات...", "loading");
197
- mergeBtn.disabled = true;
198
- mergeBtn.classList.add("disabled");
199
-
200
- if (mode === "images") {
201
- const pdfDoc = await PDFLib.PDFDocument.create();
202
-
203
- for (const file of files) {
204
- const bytes = await file.arrayBuffer();
205
- const lower = file.name.toLowerCase();
206
- let image;
207
-
208
- if (
209
- file.type === "image/jpeg" ||
210
- file.type === "image/jpg" ||
211
- lower.endsWith(".jpg") ||
212
- lower.endsWith(".jpeg")
213
- ) {
214
- image = await pdfDoc.embedJpg(bytes);
215
- } else {
216
- image = await pdfDoc.embedPng(bytes);
217
- }
218
-
219
- const imgWidth = image.width;
220
- const imgHeight = image.height;
221
- const pageWidth = 595.28; // A4
222
- const pageHeight = 841.89; // A4
223
 
224
- const page = pdfDoc.addPage([pageWidth, pageHeight]);
225
- const scale = Math.min(pageWidth / imgWidth, pageHeight / imgHeight);
226
- const drawWidth = imgWidth * scale;
227
- const drawHeight = imgHeight * scale;
228
- const x = (pageWidth - drawWidth) / 2;
229
- const y = (pageHeight - drawHeight) / 2;
230
 
231
- page.drawImage(image, { x, y, width: drawWidth, height: drawHeight });
232
- }
 
 
 
 
233
 
234
- const pdfBytes = await pdfDoc.save();
235
- const outName =
236
- (outputNameInput.value || "merged-images.pdf").trim() ||
237
- "merged-images.pdf";
238
- downloadPdf(pdfBytes, outName);
239
- setStatus("تم إنشاء ملف PDF من الصور بنجاح.", "ok");
240
  }
241
 
242
- if (mode === "pdfs") {
243
- const pdfDoc = await PDFLib.PDFDocument.create();
244
-
245
- for (const file of files) {
246
- const bytes = await file.arrayBuffer();
247
- const donorPdf = await PDFLib.PDFDocument.load(bytes);
248
- const pages = await pdfDoc.copyPages(
249
- donorPdf,
250
- donorPdf.getPageIndices()
251
- );
252
- pages.forEach(p => pdfDoc.addPage(p));
253
- }
254
 
255
- const pdfBytes = await pdfDoc.save();
256
- const outName =
257
- (outputNameInput.value || "merged-pdfs.pdf").trim() ||
258
- "merged-pdfs.pdf";
259
- downloadPdf(pdfBytes, outName);
260
- setStatus("تم دمج ملفات PDF بنجاح.", "ok");
 
 
 
 
 
261
  }
262
 
263
- // إعادة التهيئة
264
- filesInput.value = "";
265
- renderFileList([]);
266
- } catch (err) {
267
- console.error(err);
268
- setStatus("حدث خطأ أثناء المعالجة. تأكد من الملفات وحاول مرة أخ��ى.", "error");
269
- } finally {
270
- mergeBtn.disabled = false;
271
- mergeBtn.classList.remove("disabled");
272
  }
273
- });
274
- </script>
275
- </body>
276
- </html>
 
 
 
 
 
 
 
 
 
 
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>