PDF / index.html
stat2025's picture
Update index.html
d5229a7 verified
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8" />
<title>أدمـج ملـفاتك بسهولـة</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- خط Tajawal -->
<link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css" />
<!-- مكتبة pdf-lib للدمج داخل المتصفح -->
<script src="https://unpkg.com/[email protected]/dist/pdf-lib.min.js"></script>
</head>
<body>
<div class="page">
<!-- الهيدر العلوي -->
<header class="topbar">
<span class="credit">تصميم وإعداد الدعم الفني: نوف الناصر</span>
</header>
<!-- المحتوى الرئيسي -->
<main class="main">
<!-- هيرو تعريفي -->
<section class="hero">
<div class="logo-mark">PDF</div>
<h1>أدمـج ملـفاتك بسهولـة</h1>
</section>
<!-- خطوات سريعة -->
<section class="steps">
<div class="step">
<span class="step-number">1</span>
<span class="step-text">اختر الملفات (يمكن الإضافة على دفعات من الجوال أو الكمبيوتر).</span>
</div>
<div class="step">
<span class="step-number">2</span>
<span class="step-text">رتّب الملفات باستخدام الأسهم أو احذف ما لا تحتاجه.</span>
</div>
<div class="step">
<span class="step-number">3</span>
<span class="step-text">اضغط دمج، وانتظر حتى يكتمل الشريط لتحميل ملف PDF النهائي.</span>
</div>
</section>
<!-- الكارد الرئيسي -->
<section class="card main-card">
<!-- القسم 1: اختيار الملفات -->
<div class="card-section card-select">
<h2 class="card-title"> اختيار الملفات المراد دمجها بي دي اف او صور او الاثنين معاً</h2>
<label class="file-picker">
<span class="file-picker-icon">📂</span>
<span class="file-picker-text">اضغط لاختيار الملفات (يمكن التكرار والإضافة لاحقًا)</span>
<input id="files" type="file" multiple accept=".pdf,image/*" />
</label>
</div>
<!-- القسم 2: قائمة الملفات -->
<div class="card-section">
<div id="fileList" class="file-list hidden"></div>
</div>
<!-- القسم 3: الإخراج والأزرار -->
<div class="card-section card-output">
<div class="card-row inline">
<label for="outputName" class="card-label">
اسم ملف الإخراج (اختياري)
<span class="label-note">
في حال تركه فارغًا سيتم استخدام تاريخ اليوم تلقائيًا،
<strong>مثال: 2025-11-12.pdf</strong>
</span>
</label>
<input
id="outputName"
type="text"
class="output-input"
placeholder="اكتب اسم الملف هنا أو اتركه فارغًا لاستخدام تاريخ اليوم"
/>
</div>
<div class="actions">
<button id="mergeBtn" class="btn-main">دمج وإنشاء ملف PDF واحد</button>
<button id="clearBtn" class="btn-secondary" type="button">مسح جميع الملفات</button>
</div>
<div id="status" class="status"></div>
<div id="progress" class="progress hidden">
<div id="progressText" class="progress-text">جاري المعالجة...</div>
<div class="progress-bar">
<div id="progressFill" class="progress-fill"></div>
</div>
</div>
</div>
</section>
</main>
</div>
<script>
const filesInput = document.getElementById("files");
const mergeBtn = document.getElementById("mergeBtn");
const clearBtn = document.getElementById("clearBtn");
const statusDiv = document.getElementById("status");
const fileListDiv = document.getElementById("fileList");
const outputNameInput = document.getElementById("outputName");
const progressDiv = document.getElementById("progress");
const progressText = document.getElementById("progressText");
const progressFill = document.getElementById("progressFill");
let selectedFiles = [];
const MAX_RECOMMENDED_FILES = 200;
// تاريخ اليوم بصيغة YYYY-MM-DD
function getTodayDateString() {
const d = new Date();
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
function setStatus(msg, type = "") {
statusDiv.textContent = msg;
statusDiv.className = "status" + (type ? " " + type : "");
if (!msg) statusDiv.className = "status";
}
function showProgress(show) {
if (show) {
progressDiv.classList.remove("hidden");
progressFill.style.width = "0%";
progressText.textContent = "جاري المعالجة...";
} else {
progressDiv.classList.add("hidden");
}
}
function setProgress(current, total, label = "معالجة الملفات") {
if (!total || total < 1) total = 1;
const percent = Math.floor((current / total) * 100);
progressFill.style.width = percent + "%";
progressText.textContent = `${label} (${current} من ${total}) - ${percent}%`;
}
function isImage(file) {
const name = file.name.toLowerCase();
return (
file.type.startsWith("image/") ||
name.endsWith(".jpg") ||
name.endsWith(".jpeg") ||
name.endsWith(".png")
);
}
function isPDF(file) {
const name = file.name.toLowerCase();
return (
file.type === "application/pdf" ||
name.endsWith(".pdf")
);
}
function getFilesInfo(files) {
let hasImages = false;
let hasPDFs = false;
files.forEach((f) => {
if (isImage(f)) hasImages = true;
else if (isPDF(f)) hasPDFs = true;
});
return { hasImages, hasPDFs };
}
function renderFileList(files) {
if (!files.length) {
fileListDiv.classList.add("hidden");
fileListDiv.innerHTML = "";
return;
}
fileListDiv.classList.remove("hidden");
const { hasImages, hasPDFs } = getFilesInfo(files);
let modeText = "";
if (hasImages && hasPDFs) {
modeText = "الوضع الحالي: دمج صور + ملفات PDF في ملف واحد، مع الحفاظ على المقاس الأصلي لكل صفحة.";
} else if (hasPDFs) {
modeText = "الوضع الحالي: دمج ملفات PDF في ملف واحد (نسخ الصفحات كما هي).";
} else if (hasImages) {
modeText = "الوضع الحالي: تحويل صور إلى PDF مع إنشاء صفحة بكل صورة وبنفس أبعادها الأصلية.";
} else {
modeText = "لا توجد ملفات مدعومة في القائمة.";
}
fileListDiv.innerHTML = `
<div class="file-list-header">
<span>الملفات المختارة: ${files.length}</span>
<span class="file-note">يمكنك إعادة الترتيب أو حذف أي ملف قبل الدمج.</span>
</div>
<div class="mode-label">${modeText}</div>
<ul class="file-list-ul">
${files
.map(
(f, i) => `
<li>
<span class="index">${i + 1}</span>
<span class="name" title="${f.name}">${f.name}</span>
<span class="size">${(f.size / 1024).toFixed(1)} كيلوبايت</span>
<div class="row-actions">
<button class="move-btn" data-index="${i}" data-dir="up" title="نقل لأعلى">↑</button>
<button class="move-btn" data-index="${i}" data-dir="down" title="نقل لأسفل">↓</button>
<button class="delete-btn" data-index="${i}" title="حذف الملف">×</button>
</div>
</li>`
)
.join("")}
</ul>
`;
if (files.length > MAX_RECOMMENDED_FILES) {
setStatus(
"تنبيه: عدد الملفات كبير، قد تستغرق عملية الدمج وقتًا أطول على بعض الأجهزة.",
"warning"
);
}
// حذف ملف
fileListDiv.querySelectorAll(".delete-btn").forEach((btn) => {
btn.addEventListener("click", (e) => {
const index = parseInt(e.currentTarget.dataset.index, 10);
if (!isNaN(index)) {
selectedFiles.splice(index, 1);
renderFileList(selectedFiles);
if (!selectedFiles.length) {
setStatus("");
showProgress(false);
}
}
});
});
// إعادة الترتيب
fileListDiv.querySelectorAll(".move-btn").forEach((btn) => {
btn.addEventListener("click", (e) => {
const index = parseInt(e.currentTarget.dataset.index, 10);
const dir = e.currentTarget.dataset.dir;
if (isNaN(index)) return;
if (dir === "up" && index > 0) {
[selectedFiles[index - 1], selectedFiles[index]] =
[selectedFiles[index], selectedFiles[index - 1]];
} else if (dir === "down" && index < selectedFiles.length - 1) {
[selectedFiles[index + 1], selectedFiles[index]] =
[selectedFiles[index], selectedFiles[index + 1]];
}
renderFileList(selectedFiles);
});
});
}
function downloadPdf(bytes, filename) {
const blob = new Blob([bytes], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
// إضافة ملفات على دفعات
filesInput.addEventListener("change", () => {
const newFilesRaw = Array.from(filesInput.files || []);
if (!newFilesRaw.length) return;
const newFiles = newFilesRaw.filter((f) => isImage(f) || isPDF(f));
const map = new Map();
[...selectedFiles, ...newFiles].forEach((f) => {
const key = `${f.name}|${f.size}|${f.lastModified}`;
if (!map.has(key)) map.set(key, f);
});
selectedFiles = Array.from(map.values());
renderFileList(selectedFiles);
setStatus("");
filesInput.value = "";
});
// مسح الكل
clearBtn.addEventListener("click", () => {
selectedFiles = [];
renderFileList([]);
setStatus("تم مسح جميع الملفات المختارة.", "ok");
filesInput.value = "";
showProgress(false);
});
// عملية الدمج
mergeBtn.addEventListener("click", async () => {
const files = [...selectedFiles];
if (!files.length) {
setStatus("الرجاء اختيار الملفات أولاً.", "error");
return;
}
const unsupported = files.filter((f) => !isImage(f) && !isPDF(f));
if (unsupported.length) {
setStatus("يوجد ملفات غير مدعومة. يرجى حذفها من القائمة.", "error");
return;
}
const { hasImages, hasPDFs } = getFilesInfo(files);
if (!hasImages && !hasPDFs) {
setStatus("لا توجد ملفات مدعومة للدمج.", "error");
return;
}
renderFileList(files);
try {
setStatus("جاري معالجة الملفات...", "loading");
showProgress(true);
mergeBtn.disabled = true;
mergeBtn.classList.add("disabled");
filesInput.disabled = true;
clearBtn.disabled = true;
const pdfDoc = await PDFLib.PDFDocument.create();
const totalSteps = files.length;
let currentStep = 0;
for (const file of files) {
const bytes = await file.arrayBuffer();
if (isPDF(file)) {
// نسخ صفحات PDF كما هي
const donorPdf = await PDFLib.PDFDocument.load(bytes);
const pages = await pdfDoc.copyPages(
donorPdf,
donorPdf.getPageIndices()
);
pages.forEach((p) => pdfDoc.addPage(p));
} else if (isImage(file)) {
// إدراج الصورة في صفحة جديدة بنفس أبعاد الصورة (بدون تغيير المقاس)
const lower = file.name.toLowerCase();
let image;
if (
file.type === "image/jpeg" ||
file.type === "image/jpg" ||
lower.endsWith(".jpg") ||
lower.endsWith(".jpeg")
) {
image = await pdfDoc.embedJpg(bytes);
} else {
image = await pdfDoc.embedPng(bytes);
}
const imgW = image.width;
const imgH = image.height;
// صفحة بنفس أبعاد الصورة
const page = pdfDoc.addPage([imgW, imgH]);
page.drawImage(image, { x: 0, y: 0, width: imgW, height: imgH });
}
currentStep += 1;
setProgress(currentStep, totalSteps);
}
const pdfBytes = await pdfDoc.save();
// الاسم الافتراضي: تاريخ اليوم إذا لم يُكتب اسم
const defaultName = getTodayDateString() + ".pdf";
const outName =
(outputNameInput.value || defaultName).trim() || defaultName;
downloadPdf(pdfBytes, outName);
setStatus("تم إنشاء ملف PDF النهائي بنجاح.", "ok");
showProgress(false);
selectedFiles = [];
renderFileList([]);
filesInput.value = "";
} catch (err) {
console.error(err);
setStatus("حدث خطأ أثناء المعالجة. تأكد من الملفات وحاول مرة أخرى.", "error");
showProgress(false);
} finally {
mergeBtn.disabled = false;
mergeBtn.classList.remove("disabled");
filesInput.disabled = false;
clearBtn.disabled = false;
}
});
</script>
</body>
</html>