نسخة موحدة للنموذج مع الطباعة والتوقيع والإرسال بالبريد
COMPANY ACCOMMODATION INSPECTION REPORT
تقرير تفتيش سكن الشركة
INSPECTION NAME INFORMATION
EMPLOYEE Noالرقم الوظيفي DEPARTMENTالقسم
NAMEالاسم PROJECT NAMEاسم المشروع
POSITIONالمسمى الوظيفي DATE & TIMEالوقت والتاريخ

GENERAL INFORMATION
TOTAL NUMBER OF EMPLOYEEالعدد الكامل للموظفين BUILDING TYPEنوع المبنى
NUMBER OF ROOMSعدد الغرف NUMBER OF FLOORSعدد الطوابق
Check (✔) YES if the item is available, in good condition, clean, & functioning properly
Check (✔) NO if the item is not available.
Check (✔) POOR if the item is available but damaged, dirty, or not functioning properly.
ضع علامة (✔) نعم (YES) إذا كان العنصر متوفرًا، بحالة جيدة، نظيفًا، ويعمل بشكل صحيح.
ضع علامة (✔) لا (NO) إذا كان العنصر غير متوفر.
ضع علامة (✔) سيئ (POOR) إذا كان العنصر متوفرًا لكنه تالف، متسخ، أو لا يعمل بشكل صحيح.

1. Occupancy & IdentificationYESNOPOORالإشغال والتعريف
• Is the room occupied by the assigned employee only? هل الغرفة مشغولة من قبل الموظف المخصص فقط؟
• Are there any individuals living in the room not sponsored by 3S Company? هل يوجد أي أشخاص يقيمون في الغرفة غير مكفولين من قبل شركة 3S؟
• If not under 3S Company sponsorship, is there an approved authorization? إذا لم يكونوا على كفالة شركة 3S، هل يوجد تصريح معتمد؟
2. Housekeeping & CleanlinessYESNOPOORالنظافة والترتيب
• Is the room cleaned regularly (daily/weekly)? هل يتم تنظيف الغرفة بشكل منتظم (يومي/أسبوعي)؟
• Are there any unpleasant odors? هل توجد أي روائح كريهة؟
• Are there any available AC? هل يوجد مكيف متوفر؟
• Are the walls in good condition (no cracks or peeling paint)? هل الجدران بحالة جيدة (بدون تشققات أو تقشر في الطلاء)؟
• Is the floor clean and undamaged? هل الأرضية نظيفة وغير متضررة؟
• Are there any water leaks? هل توجد أي تسربات مياه؟
• Are cleaning materials available (detergent, mop, broom)? هل تتوفر مواد التنظيف (منظف، ممسحة، مكنسة)؟
3. FurnitureYESNOPOORالأثاث
• Is the bed in good condition? هل السرير بحالة جيدة؟
• Is the mattress clean and not damaged? هل المرتبة نظيفة وغير متضررة؟
• Are there pillows & blankets available? هل تتوفر الوسائد والبطانيات؟
• Are any rooms overcrowded? هل هناك أي غرف مزدحمة بشكل زائد؟
4. Health & HygieneYESNOPOORالصحة والنظافة
• Are cooking areas clean and safe (if inside room)? هل مناطق الطبخ نظيفة وآمنة (إذا كانت داخل الغرفة)؟
• Is food stored properly (no spoilage or contamination)? هل يتم تخزين الطعام بشكل صحيح (بدون تلف أو تلوث)؟
• Are there proper ventilation and airflow? هل يوجد تهوية جيدة وتدفق هواء مناسب؟
Page 1 of 2
5. BathroomYESNOPOORدورة المياه
• Is the toilet clean and functional? هل دورة المياه نظيفة وتعمل بشكل جيد؟
• Are the sink and taps working properly? هل المغسلة والصنابير تعمل بشكل صحيح؟
• Is the shower operational? هل الدش (الشاور) يعمل؟
• Are there any water leaks? هل توجد أي تسربات مياه؟
• Is the water supply adequate? هل إمداد المياه كافٍ؟
• Is the drainage system working properly? هل نظام التصريف (الصرف الصحي) يعمل بشكل صحيح؟
6. Maintenance & RepairsYESNOPOORالصيانة والإصلاحات
• Are there any pending maintenance issues? هل توجد أي مشاكل صيانة معلقة؟
• Are doors, locks, and hinges in good working condition? هل الأبواب والأقفال والمفصلات بحالة عمل جيدة؟
• Are plumbing fixtures free from damage? هل أدوات السباكة خالية من أي تلف؟
7. Electrical & LightingYESNOPOORالكهرباء والإضاءة
• Are all lights working properly? هل جميع الإضاءة تعمل بشكل صحيح؟
• Are the power sockets functioning? هل الأفياش الكهربائية تعمل؟
• Are there any exposed wires? هل توجد أي أسلاك مكشوفة؟
8. SafetyYESNOPOORالسلامة
• Is a smoke detector installed and working? هل تم تركيب كاشف الدخان ويعمل بشكل صحيح؟
• Is a fire extinguisher available (if required)? هل طفاية الحريق متوفرة (إذا كانت مطلوبة)؟
• Is emergency exit information posted? هل معلومات مخارج الطوارئ معلقة وموجودة؟
• Is the door lock functioning properly? هل قفل الباب يعمل بشكل صحيح؟
• Are the windows secure and lockable? هل النوافذ آمنة ويمكن إغلاقها بإحكام؟
9. Violations & Observations Yes No المخالفات والملاحظات
Any policy violations identified? If YES, specify إذا كانت الإجابة نعم، يرجى التوضيح هل تم رصد أي مخالفات للسياسة
10. Inspector Remarks ملاحظات التفتيش
11. Acknowledgement إقرار
I confirm that the above inspection has been conducted, and findings are accurate. أؤكد أنه تم إجراء الفحص أعلاه، وأن النتائج دقيقة.
Employee Responsible for Inspection/الموظف المسؤول عن التفتيش Project Manager Approval/ اعتماد مدير المشروع
Name: الاسم: Name: الاسم:
Signature: Employee Signature التوقيع: Signature: Project Manager Signature التوقيع:
Date: التاريخ: Date: التاريخ:
Page 2 of 2
FP-19-19

Vehicle Handover and Receiving Form

نموذج تسليم واستلام مركبة

REV.01
Date / التاريخ  
Today / اليوم  
Vehicle Typeالمركبة نوع Plate Numberاللوحة رقم Model Yearالموديل سنة Projectالمشروع Odometer (Km)(كم) العداد قراءة
Accessories / الملحقات
نعم / YESلا / NOنعم / YESلا / NOنعم / YESلا / NO
Fuel Chipالوقود شريحةFirst Aid Kitحقيبة الإسعافات الأوليةSpare Tireالاحتياطي الإطار
Ignition Keyالتشغيل مفتاحFire Extinguisherحريق طفايةSeat Beltالأمان حزام
Vehicle Plateالمركبة لوحةWarning Triangleالتحذير مثلثJack & Wheel Wrenchعجلات ومفتاح رافعة
Vehicle RemarksBody / المركبة هيكلالسيارة ملاحظات
1-
2-
3-
4-
5-
حدّد أماكن الأضرار والخدوش على الرسم
Vehicle body diagram
6-
7-
8-
9-
10-
Acknowledgment and Undertakingتعهد إقرار
I, the undersigned, acknowledge that I have received the above-mentioned vehicle in sound and good condition, that all remarks noted above have been duly recorded, and I accept full responsibility for the vehicle throughout the period of receipt.أقرّ أنا الموقع أدناه بأنني استلمت المركبة الموضّحة أعلاه بحالة سليمة وجيدة، وقد تم ذكر جميع الملاحظات أعلاه، وأتحمّل كامل المسؤولية عنها طوال فترة الاستلام.
I undertake to use the vehicle strictly for official purposes only and to comply with the company’s policies and all applicable traffic laws and regulations in the Kingdom of Saudi Arabia. I also undertake to immediately report any accident, damage, or malfunction affecting the vehicle and to adhere to the company’s approved procedures.أتعهد باستخدام المركبة للأغراض الرسمية فقط، والالتزام بسياسات الشركة وكافة أنظمة وتعليمات المرور المعمول بها في المملكة العربية السعودية، كما أتعهد بالإبلاغ فورًا عن أي حادث أو ضرر أو عطل يطرأ على المركبة، والالتزام بالإجراءات المعتمدة لدى الشركة.
It is strictly prohibited to hand over, authorize, or allow any other person to drive or use the vehicle without prior written approval from the Human Resources Department (HR). The authorized employee shall remain solely responsible for the vehicle.يحظر تسليم أو تمكين أي شخص آخر من قيادة أو استخدام المركبة دون موافقة كتابية مسبقة من إدارة الموارد البشرية (HR)، ويظل الموظف المفوض المسؤول الوحيد عن المركبة.
I agree to bear the full cost of any traffic violations, damages, or losses resulting from misuse or negligence and authorize the Company to deduct such amounts from my salary or entitlements if applicable.أتحمل كامل تكاليف أي مخالفات مرورية أو أضرار أو خسائر ناتجة عن سوء الاستخدام أو الإهمال، وأفوض الشركة بخصمها من راتبي أو مستحقاتي عند الاقتضاء.
I confirm that I have read, understood, and agree to comply with the Company Vehicle Usage Policy.أقرّ بأنني اطلعت على سياسة استخدام مركبات الشركة وفهمتها وأتعهد بالالتزام بها.
Vehicle Recipient / مستلم المركبة
Name:الاسم:
Employee ID:الرقم الوظيفي :
National ID / Iqama No:رقم الهوية / الإقامة :
Date:التاريخ :
Time:الوقت :
Signature:التوقيع :
Vehicle Issuer / مسلِّم المركبة
Name:الاسم:
Employee ID:الرقم الوظيفي :
National ID / Iqama No:رقم الهوية / الإقامة :
Date:التاريخ :
Time:الوقت :
Signature:التوقيع :
Designated for Project Manager Approval of Vehicle Handover
مخصّص لمدير المشروع للموافقة على تسليم المركبة
I approve the transfer of the vehicle and confirm that it is in good condition, with no remarks other than those stated above.YesNOI do not approve the transfer of the vehicle due to the following remarks:
Project Manager / مدير المشروع
Name:
أوافق على نقل المركبة، وأؤكد أن المركبة سليمة ولا توجد أي ملاحظات أخرى غير المذكورة أعلاه.لا أوافق على نقل المركبة، وذلك لوجود الملاحظات التالية.Date:
Signature:
'; } async function generateReportPdfBlob() { if (typeof html2canvas === 'undefined' || !window.jspdf || !window.jspdf.jsPDF) { throw new Error('PDF_LIB_NOT_LOADED'); } const pages = Array.from(document.querySelectorAll('form .page')); if (!pages.length) throw new Error('NO_PAGES'); const jsPDF = window.jspdf.jsPDF; const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4', compress: true }); document.body.classList.add('generating-pdf'); await new Promise(function(resolve) { setTimeout(resolve, 180); }); try { for (let i = 0; i < pages.length; i++) { const canvas = await html2canvas(pages[i], { scale: 2, backgroundColor: '#ffffff', useCORS: true, allowTaint: true, logging: false, scrollX: 0, scrollY: -window.scrollY, windowWidth: document.documentElement.scrollWidth }); const imgData = canvas.toDataURL('image/jpeg', 0.98); if (i > 0) pdf.addPage('a4', 'p'); pdf.addImage(imgData, 'JPEG', 0, 0, 210, 297, undefined, 'FAST'); } return pdf.output('blob'); } finally { document.body.classList.remove('generating-pdf'); } } async function sendEmailReport() { const sender = (document.getElementById('emailSender') || {}).value || ''; const additional = (document.getElementById('emailAdditional') || {}).value || ''; const cc = (document.getElementById('emailCc') || {}).value || ''; const note = (document.getElementById('emailNote') || {}).value || ''; if (!isValidEmailAddress(sender)) { setEmailStatus('يرجى إدخال إيميل المرسل بشكل صحيح.', 'err'); return; } if (additional.trim() && hasInvalidEmailList(additional)) { setEmailStatus('يوجد إيميل إضافي غير صحيح. يرجى المراجعة.', 'err'); return; } if (cc.trim() && hasInvalidEmailList(cc)) { setEmailStatus('يوجد إيميل CC غير صحيح. يرجى المراجعة.', 'err'); return; } const reportHtml = buildReportHtmlForEmail(note); if (!reportHtml) { setEmailStatus('تعذر تجهيز النموذج للإرسال.', 'err'); return; } try { if (sendEmailButton) sendEmailButton.disabled = true; setEmailStatus('جاري تجهيز ملف PDF للنموذج...', 'info'); const pdfBlob = await generateReportPdfBlob(); setEmailStatus('تم تجهيز PDF، جاري الإرسال...', 'info'); const formData = new FormData(); formData.append('sender_email', sender.trim()); formData.append('additional_email', additional.trim()); formData.append('cc_email', cc.trim()); formData.append('note', note); formData.append('report_html', reportHtml); formData.append('report_pdf', pdfBlob, 'company_accommodation_inspection_report.pdf'); const response = await fetch('send_accommodation_report_email.php', { method: 'POST', body: formData, credentials: 'same-origin' }); let result = null; try { result = await response.json(); } catch (e) { result = null; } if (response.ok && result && result.success) { setEmailStatus('تم إرسال النموذج كملف PDF بنجاح.', 'ok'); } else { const message = result && result.message ? result.message : 'تعذر الإرسال. تأكد من رفع ملف PHP في نفس المجلد ومن تفعيل البريد على السيرفر.'; setEmailStatus(message, 'err'); } } catch (error) { if (error && error.message === 'PDF_LIB_NOT_LOADED') { setEmailStatus('تعذر تحميل مكتبة PDF. تأكد من اتصال المتصفح بالإنترنت أو ارفع مكتبات html2canvas و jsPDF محليًا.', 'err'); } else { setEmailStatus('تعذر تجهيز أو إرسال ملف PDF. ارفع الملف على السيرفر ولا تفتحه من الجهاز مباشرة.', 'err'); } } finally { if (sendEmailButton) sendEmailButton.disabled = false; } } if (emailModal) { emailModal.addEventListener('click', function(event) { if (event.target === emailModal) closeEmailModal(); }); } function resetForm() { if (confirm('سيتم مسح جميع البيانات المدخلة. هل تريد المتابعة؟')) { document.querySelector('form').reset(); document.querySelectorAll('.signature-cell').forEach(function(cell) { cell.classList.remove('signed'); const img = cell.querySelector('.signature-image'); const input = cell.querySelector('.signature-text'); if (img) { img.removeAttribute('src'); img.removeAttribute('style'); } if (input) input.value = ''; delete cell.dataset.sigSize; delete cell.dataset.sigX; delete cell.dataset.sigY; }); clearSignaturePad(); clearUploadedSignature(); } } const signatureModal = document.getElementById('signatureModal'); const signatureCanvas = document.getElementById('signatureCanvas'); const signatureCtx = signatureCanvas.getContext('2d'); const signatureUpload = document.getElementById('signatureUpload'); const signatureUploadPreview = document.getElementById('signatureUploadPreview'); const signatureInsertMode = document.getElementById('signatureInsertMode'); const signatureSize = document.getElementById('signatureSize'); const signatureX = document.getElementById('signatureX'); const signatureY = document.getElementById('signatureY'); const signatureSizeValue = document.getElementById('signatureSizeValue'); const signatureXValue = document.getElementById('signatureXValue'); const signatureYValue = document.getElementById('signatureYValue'); let signatureDrawing = false; let signatureHasInk = false; let lastPoint = null; let signatureBounds = null; let uploadedSignatureDataUrl = null; const SIGNATURE_INK_COLOR = '#0047AB'; const SIGNATURE_DEFAULT_PLACEMENT = { size:100, x:0, y:0 }; function getSignatureTargetCell() { const targetSelect = document.getElementById('signatureTarget'); if (!targetSelect) return null; return document.querySelector('.signature-cell[data-signature-target="' + targetSelect.value + '"]'); } function getPlacementFromControls() { return { size: signatureSize ? Number(signatureSize.value || SIGNATURE_DEFAULT_PLACEMENT.size) : SIGNATURE_DEFAULT_PLACEMENT.size, x: signatureX ? Number(signatureX.value || SIGNATURE_DEFAULT_PLACEMENT.x) : SIGNATURE_DEFAULT_PLACEMENT.x, y: signatureY ? Number(signatureY.value || SIGNATURE_DEFAULT_PLACEMENT.y) : SIGNATURE_DEFAULT_PLACEMENT.y }; } function setControlsFromPlacement(placement) { const p = placement || SIGNATURE_DEFAULT_PLACEMENT; if (signatureSize) signatureSize.value = p.size; if (signatureX) signatureX.value = p.x; if (signatureY) signatureY.value = p.y; updatePlacementLabels(); } function updatePlacementLabels() { const p = getPlacementFromControls(); if (signatureSizeValue) signatureSizeValue.textContent = p.size + '%'; if (signatureXValue) signatureXValue.textContent = p.x; if (signatureYValue) signatureYValue.textContent = p.y; } function savePlacementToCell(cell, placement) { if (!cell) return; const p = placement || getPlacementFromControls(); cell.dataset.sigSize = String(p.size); cell.dataset.sigX = String(p.x); cell.dataset.sigY = String(p.y); } function getPlacementFromCell(cell) { if (!cell) return Object.assign({}, SIGNATURE_DEFAULT_PLACEMENT); return { size: Number(cell.dataset.sigSize || SIGNATURE_DEFAULT_PLACEMENT.size), x: Number(cell.dataset.sigX || SIGNATURE_DEFAULT_PLACEMENT.x), y: Number(cell.dataset.sigY || SIGNATURE_DEFAULT_PLACEMENT.y) }; } function applyPlacementToImage(img, placement) { if (!img) return; const p = placement || getPlacementFromControls(); img.style.setProperty('--sig-w', p.size + '%'); img.style.setProperty('--sig-h', Math.max(38, Math.round(64 * p.size / 100)) + 'px'); img.style.setProperty('--sig-x', p.x + 'px'); img.style.setProperty('--sig-y', p.y + 'px'); } function applyCurrentPlacementPreview() { updatePlacementLabels(); const cell = getSignatureTargetCell(); if (!cell || !cell.classList.contains('signed')) return; const img = cell.querySelector('.signature-image'); const placement = getPlacementFromControls(); applyPlacementToImage(img, placement); savePlacementToCell(cell, placement); } function loadPlacementForSelectedTarget() { const cell = getSignatureTargetCell(); setControlsFromPlacement(getPlacementFromCell(cell)); } function resetSignaturePlacement() { setControlsFromPlacement(Object.assign({}, SIGNATURE_DEFAULT_PLACEMENT)); applyCurrentPlacementPreview(); } function applySignaturePlacementOnly() { const cell = getSignatureTargetCell(); if (!cell || !cell.classList.contains('signed')) { alert('اختر موقعًا يحتوي على توقيع أولاً، أو أدرج التوقيع ثم عدّل الموقع والحجم.'); return; } applyCurrentPlacementPreview(); } ['input','change'].forEach(function(evtName) { [signatureSize, signatureX, signatureY].forEach(function(control) { if (control) control.addEventListener(evtName, applyCurrentPlacementPreview); }); }); const signatureTargetSelect = document.getElementById('signatureTarget'); if (signatureTargetSelect) { signatureTargetSelect.addEventListener('change', loadPlacementForSelectedTarget); } function prepareSignaturePad() { signatureCtx.lineCap = 'round'; signatureCtx.lineJoin = 'round'; signatureCtx.strokeStyle = SIGNATURE_INK_COLOR; signatureCtx.lineWidth = 7.5; signatureCtx.globalAlpha = 1; signatureCtx.shadowColor = 'rgba(0,71,171,.18)'; signatureCtx.shadowBlur = 0.8; signatureCtx.imageSmoothingEnabled = true; } prepareSignaturePad(); function updateSignatureBounds(point) { const pad = 24; if (!signatureBounds) { signatureBounds = { minX: point.x - pad, minY: point.y - pad, maxX: point.x + pad, maxY: point.y + pad }; return; } signatureBounds.minX = Math.min(signatureBounds.minX, point.x - pad); signatureBounds.minY = Math.min(signatureBounds.minY, point.y - pad); signatureBounds.maxX = Math.max(signatureBounds.maxX, point.x + pad); signatureBounds.maxY = Math.max(signatureBounds.maxY, point.y + pad); } function openSignatureModal() { signatureModal.classList.add('open'); signatureModal.setAttribute('aria-hidden', 'false'); loadPlacementForSelectedTarget(); setTimeout(function() { prepareSignaturePad(); }, 30); } function closeSignatureModal() { signatureModal.classList.remove('open'); signatureModal.setAttribute('aria-hidden', 'true'); } function clearSignaturePad() { signatureCtx.clearRect(0, 0, signatureCanvas.width, signatureCanvas.height); prepareSignaturePad(); signatureHasInk = false; lastPoint = null; signatureBounds = null; } function getCanvasPoint(event) { const rect = signatureCanvas.getBoundingClientRect(); const clientX = event.touches ? event.touches[0].clientX : event.clientX; const clientY = event.touches ? event.touches[0].clientY : event.clientY; return { x: (clientX - rect.left) * (signatureCanvas.width / rect.width), y: (clientY - rect.top) * (signatureCanvas.height / rect.height) }; } function startSignatureDraw(event) { event.preventDefault(); uploadedSignatureDataUrl = null; if (signatureUpload) signatureUpload.value = ''; if (signatureUploadPreview) { signatureUploadPreview.removeAttribute('src'); signatureUploadPreview.classList.remove('ready'); } if (signatureInsertMode) signatureInsertMode.textContent = 'الوضع الحالي: الرسم اليدوي'; signatureDrawing = true; lastPoint = getCanvasPoint(event); updateSignatureBounds(lastPoint); } function drawSignature(event) { if (!signatureDrawing) return; event.preventDefault(); const point = getCanvasPoint(event); updateSignatureBounds(point); signatureCtx.beginPath(); signatureCtx.moveTo(lastPoint.x, lastPoint.y); signatureCtx.lineTo(point.x, point.y); signatureCtx.stroke(); lastPoint = point; signatureHasInk = true; } function stopSignatureDraw(event) { if (event) event.preventDefault(); signatureDrawing = false; lastPoint = null; } signatureCanvas.addEventListener('mousedown', startSignatureDraw); signatureCanvas.addEventListener('mousemove', drawSignature); signatureCanvas.addEventListener('mouseup', stopSignatureDraw); signatureCanvas.addEventListener('mouseleave', stopSignatureDraw); signatureCanvas.addEventListener('touchstart', startSignatureDraw, { passive:false }); signatureCanvas.addEventListener('touchmove', drawSignature, { passive:false }); signatureCanvas.addEventListener('touchend', stopSignatureDraw, { passive:false }); signatureCanvas.addEventListener('touchcancel', stopSignatureDraw, { passive:false }); function getCroppedSignatureDataUrl() { if (!signatureBounds) return signatureCanvas.toDataURL('image/png'); const minX = Math.max(0, Math.floor(signatureBounds.minX - 12)); const minY = Math.max(0, Math.floor(signatureBounds.minY - 12)); const maxX = Math.min(signatureCanvas.width, Math.ceil(signatureBounds.maxX + 12)); const maxY = Math.min(signatureCanvas.height, Math.ceil(signatureBounds.maxY + 12)); const sourceWidth = Math.max(1, maxX - minX); const sourceHeight = Math.max(1, maxY - minY); const outputCanvas = document.createElement('canvas'); outputCanvas.width = 900; outputCanvas.height = 240; const outputCtx = outputCanvas.getContext('2d'); outputCtx.clearRect(0, 0, outputCanvas.width, outputCanvas.height); outputCtx.imageSmoothingEnabled = true; outputCtx.imageSmoothingQuality = 'high'; const scale = Math.min(outputCanvas.width * 0.94 / sourceWidth, outputCanvas.height * 0.82 / sourceHeight); const drawW = sourceWidth * scale; const drawH = sourceHeight * scale; const dx = (outputCanvas.width - drawW) / 2; const dy = (outputCanvas.height - drawH) / 2; outputCtx.drawImage(signatureCanvas, minX, minY, sourceWidth, sourceHeight, dx, dy, drawW, drawH); return outputCanvas.toDataURL('image/png'); } function clearUploadedSignature() { uploadedSignatureDataUrl = null; if (signatureUpload) signatureUpload.value = ''; if (signatureUploadPreview) { signatureUploadPreview.removeAttribute('src'); signatureUploadPreview.classList.remove('ready'); } if (signatureInsertMode) signatureInsertMode.textContent = 'الوضع الحالي: الرسم اليدوي'; } function tintAndCropSignatureImage(image) { const canvas = document.createElement('canvas'); const maxW = 1200; const maxH = 500; const scale = Math.min(maxW / image.naturalWidth, maxH / image.naturalHeight, 1); canvas.width = Math.max(1, Math.round(image.naturalWidth * scale)); canvas.height = Math.max(1, Math.round(image.naturalHeight * scale)); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(image, 0, 0, canvas.width, canvas.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; let minX = canvas.width, minY = canvas.height, maxX = 0, maxY = 0; for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const i = (y * canvas.width + x) * 4; const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3]; const darkness = 255 - ((r + g + b) / 3); const visible = a > 25 && darkness > 28; if (visible) { const alpha = Math.min(255, Math.max(85, darkness * 1.65)); data[i] = 0; data[i + 1] = 71; data[i + 2] = 171; data[i + 3] = alpha; minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); maxY = Math.max(maxY, y); } else { data[i + 3] = 0; } } } ctx.putImageData(imageData, 0, 0); if (minX > maxX || minY > maxY) return null; const pad = 22; minX = Math.max(0, minX - pad); minY = Math.max(0, minY - pad); maxX = Math.min(canvas.width, maxX + pad); maxY = Math.min(canvas.height, maxY + pad); const cropW = Math.max(1, maxX - minX); const cropH = Math.max(1, maxY - minY); const outputCanvas = document.createElement('canvas'); outputCanvas.width = 900; outputCanvas.height = 240; const outputCtx = outputCanvas.getContext('2d'); outputCtx.clearRect(0, 0, outputCanvas.width, outputCanvas.height); outputCtx.imageSmoothingEnabled = true; outputCtx.imageSmoothingQuality = 'high'; const outScale = Math.min(outputCanvas.width * 0.94 / cropW, outputCanvas.height * 0.82 / cropH); const drawW = cropW * outScale; const drawH = cropH * outScale; outputCtx.drawImage(canvas, minX, minY, cropW, cropH, (outputCanvas.width - drawW) / 2, (outputCanvas.height - drawH) / 2, drawW, drawH); return outputCanvas.toDataURL('image/png'); } if (signatureUpload) { signatureUpload.addEventListener('change', function(event) { const file = event.target.files && event.target.files[0]; if (!file) return; if (!file.type || !file.type.startsWith('image/')) { alert('يرجى اختيار ملف صورة فقط.'); clearUploadedSignature(); return; } const reader = new FileReader(); reader.onload = function(e) { const img = new Image(); img.onload = function() { const result = tintAndCropSignatureImage(img); if (!result) { alert('تعذر قراءة التوقيع من الصورة. يرجى اختيار صورة أوضح.'); clearUploadedSignature(); return; } uploadedSignatureDataUrl = result; if (signatureUploadPreview) { signatureUploadPreview.src = result; signatureUploadPreview.classList.add('ready'); } signatureCtx.clearRect(0, 0, signatureCanvas.width, signatureCanvas.height); prepareSignaturePad(); signatureHasInk = false; signatureBounds = null; if (signatureInsertMode) signatureInsertMode.textContent = 'الوضع الحالي: صورة توقيع جاهزة للإدراج'; }; img.src = e.target.result; }; reader.readAsDataURL(file); }); } function insertSignature() { if (!signatureHasInk && !uploadedSignatureDataUrl) { alert('يرجى رسم التوقيع أو اختيار صورة توقيع أولاً.'); return; } const target = document.getElementById('signatureTarget').value; const cell = document.querySelector('.signature-cell[data-signature-target="' + target + '"]'); if (!cell) return; const img = cell.querySelector('.signature-image'); const input = cell.querySelector('.signature-text'); img.src = uploadedSignatureDataUrl || getCroppedSignatureDataUrl(); const placement = getPlacementFromControls(); applyPlacementToImage(img, placement); savePlacementToCell(cell, placement); cell.classList.add('signed'); if (input) input.value = 'Signed'; closeSignatureModal(); } function clearInsertedSignature(target) { const cell = document.querySelector('.signature-cell[data-signature-target="' + target + '"]'); if (!cell) return; const img = cell.querySelector('.signature-image'); const input = cell.querySelector('.signature-text'); if (img) { img.removeAttribute('src'); img.removeAttribute('style'); } if (input) input.value = ''; delete cell.dataset.sigSize; delete cell.dataset.sigX; delete cell.dataset.sigY; cell.classList.remove('signed'); } let activeSignatureDrag = null; function startInsertedSignatureDrag(event) { const img = event.target.closest('.signature-image'); if (!img) return; const cell = img.closest('.signature-cell'); if (!cell || !cell.classList.contains('signed')) return; if (event.touches && event.touches.length > 1) return; event.preventDefault(); const clientX = event.touches ? event.touches[0].clientX : event.clientX; const clientY = event.touches ? event.touches[0].clientY : event.clientY; const placement = getPlacementFromCell(cell); activeSignatureDrag = { cell:cell, img:img, startX:clientX, startY:clientY, originX:placement.x, originY:placement.y, size:placement.size }; const targetSelect = document.getElementById('signatureTarget'); if (targetSelect) targetSelect.value = cell.dataset.signatureTarget; setControlsFromPlacement(placement); } function moveInsertedSignatureDrag(event) { if (!activeSignatureDrag) return; event.preventDefault(); const clientX = event.touches ? event.touches[0].clientX : event.clientX; const clientY = event.touches ? event.touches[0].clientY : event.clientY; const placement = { size: activeSignatureDrag.size, x: Math.max(-160, Math.min(160, Math.round(activeSignatureDrag.originX + (clientX - activeSignatureDrag.startX)))), y: Math.max(-45, Math.min(45, Math.round(activeSignatureDrag.originY + (clientY - activeSignatureDrag.startY)))) }; applyPlacementToImage(activeSignatureDrag.img, placement); savePlacementToCell(activeSignatureDrag.cell, placement); setControlsFromPlacement(placement); } function stopInsertedSignatureDrag() { activeSignatureDrag = null; } document.querySelectorAll('.signature-image').forEach(function(img) { img.addEventListener('mousedown', startInsertedSignatureDrag); img.addEventListener('touchstart', startInsertedSignatureDrag, { passive:false }); }); document.addEventListener('mousemove', moveInsertedSignatureDrag); document.addEventListener('touchmove', moveInsertedSignatureDrag, { passive:false }); document.addEventListener('mouseup', stopInsertedSignatureDrag); document.addEventListener('touchend', stopInsertedSignatureDrag); document.addEventListener('touchcancel', stopInsertedSignatureDrag); signatureModal.addEventListener('click', function(event) { if (event.target === signatureModal) closeSignatureModal(); }); document.addEventListener('keydown', function(event) { if (event.key === 'Escape' && signatureModal.classList.contains('open')) { closeSignatureModal(); } });