لماذا يبدو ملف PDF المحوَّل من HTML معطوباً، وكيف تصلح كل مشكلة؟

آخر تحديث: · فريق دورني

صورة عنوان عن إصلاح صفحة HTML تظهر بشكل خاطئ عند تحويلها إلى PDF

عندما لا يظهر تحويل HTML إلى PDF بشكل صحيح، يكون السبب غالباً التوقيت أو قواعد الطباعة: خطوط ورسوم بيانية التُقطت قبل اكتمال تحميلها، أو أقسام 100vh بحجم الصفحة، أو رؤوس ثابتة مكرّرة، أو خلفيات حذفها محرّك الطباعة، أو فواصل صفحات تقطع المحتوى. ولكل مشكلة حلّ مباشر في CSS أو في إعدادات المحوّل.

أولاً: حدّد نوع ملف PDF الذي تنتجه

هناك طريقتان مختلفتان تماماً لتحويل صفحة ويب إلى PDF، ولكل منهما أعطالها. خيار الطباعة / الحفظ بصيغة PDF يستخدم محرّك الطباعة في المتصفح: يبقى النص قابلاً للتحديد، لكن قواعد الطباعة تُطبَّق، فقد تختفي الخلفيات وتتكرّر الرؤوس الثابتة وتقع فواصل الصفحات حيث يقرّر المحرّك. أما ملف PDF المبني على لقطات الشاشة (لقطة بمكتبة مثل html2canvas أو html-to-image تُحفظ عبر jsPDF، مثل زر تنزيل PDF في محوّلنا المجاني من HTML إلى PDF) فينسخ الشاشة بكسلاً بكسلاً، لكن النص غير قابل للتحديد وقد تغيب الصور القادمة من مواقع أخرى.

لذا قبل أي إصلاح، لاحظ الطريقة التي أنتجت الملف المعطوب. إذا ظهرت المشكلة في إحداهما فقط، فالانتقال إلى الأخرى أحياناً هو أسرع حلّ. ويتيح المحوّل الطريقتين من المعاينة نفسها، فتصبح المقارنة سريعة. وللاطلاع على الخلفية، راجع الطرق المجانية الثلاث لتحويل HTML إلى PDF.

جدول استكشاف الأخطاء: العَرَض والسبب والحل

العَرَضالسبب المحتملالحل
خط خاطئ (Arial أو Times)خط الويب لم يُحمَّل لحظة الالتقاط أو ملفه غير متاحوسم link في head، ملفات الخطوط داخل zip، انتظار 1-2 ثانية
رسوم بيانية فارغة أو ناقصةالحركة ما زالت جارية، اللوحة بمقاس الشاشةanimation: false، انتظار إضافي، إعادة الحجم عند beforeprint
القسم الرئيسي يملأ صفحة كاملةأقسام 100vh أو h-screenheight: auto عند الطباعة، أو تثبيت 100vh على ارتفاع النافذة
الترويسة في كل صفحةمحرّك الطباعة يكرّر position: fixedposition: static عند الطباعة، أو إظهار الرؤوس الثابتة مرة واحدة
الخلفية الداكنة صارت بيضاءالمتصفحات تحذف الخلفيات عند الطباعةprint-color-adjust: exact وخيار رسومات الخلفية
أسطر أو صور مقطوعةفاصل الصفحة يقع داخل عنصرbreak-inside: avoid وفواصل صفحات ذكية
صور مفقودة (lazy)صور loading="lazy" لم تظهر في الشاشة قطloading="eager"، أو ترك المحوّل يمرّر الصفحة
صور مفقودة في PDF اللقطات فقطصور من نطاقات أخرى دون ترويسات CORSاستخدام الطباعة، أو وضع الصور داخل zip
أقسام فارغةحركات ظهور أو تمرير تبدأ من opacity 0تجميد الحركات، وجعل الظهور هو النمط الافتراضي
صفحة بلا تنسيقTailwind Play CDN لم يولّد CSS بعدالانتظار 2-3 ثوانٍ قبل الالتقاط

تشرح الأقسام التالية كل حلّ مع شيفرة جاهزة للّصق. ومعظم هذه الحلول متاحة أيضاً كخيارات في أداة تحويل HTML إلى PDF، فيمكنك غالباً إصلاح النتيجة دون لمس ملف HTML.

الخطوط: ملف PDF يستخدم Arial أو Times بدلاً من خط الويب

تُحمَّل خطوط الويب بعد ملف HTML، فيظهر الخط البديل في ملف PDF المُنشأ مبكراً، مع انكسار أسطر وعروض مختلفة. حمّل Google Fonts بوسم link داخل head (لا بأمر ‎@import مدفون في ملف أنماط). وللخطوط المستضافة لديك، استخدم مسارات نسبية وحوّل ملف ‎.zip يضمّ ملف HTML وملفات الخطوط حتى تُحلّ المسارات.

ينتظر محوّلنا خطوط المستند قبل الالتقاط؛ وإذا كان النص يُضاف بسكربت بعد التحميل، فأضف ثانية أو ثانيتين من الانتظار. واحرص دائماً على خطوط بديلة قريبة في العرض حتى لا يكسر خطٌ مفقود التخطيط.

@font-face {
  font-family: 'Brand Sans';
  src: url('fonts/brand-sans.woff2') format('woff2');
  font-weight: 400 700;
  font-display: swap;
}
body { font-family: 'Brand Sans', system-ui, sans-serif; }

الرسوم البيانية فارغة أو مرسومة جزئياً

تحرّك مكتبة Chart.js الرسوم افتراضياً لمدة ثانية تقريباً، وتُرسم اللوحة بالمقاس الذي كانت عليه في الشاشة. فإذا التُقطت مبكراً ظهرت الأعمدة ناقصة أو بنصف ارتفاعها، وإذا طُبعت على صفحة بعرض مختلف قد تخرج اللوحة ممطوطة. عطّل الحركة، وامنح حاوية الرسم ارتفاعاً ثابتاً، وأعد تحجيم الرسوم قبل الطباعة.

// The canvas sits in a container with a fixed height:
// <div style="position: relative; height: 360px"><canvas id="sales"></canvas></div>
new Chart(document.getElementById('sales'), {
  type: 'bar',
  data: {
    labels: ['Q1', 'Q2', 'Q3'],
    datasets: [{ label: 'Sales', data: [12, 19, 23] }]
  },
  options: { animation: false, responsive: true, maintainAspectRatio: false }
});

// Redraw every chart at the print size before the browser prints
window.addEventListener('beforeprint', () => {
  Object.values(Chart.instances).forEach((chart) => chart.resize());
});

مشاكل التخطيط: أقسام 100vh والرؤوس الثابتة المكرّرة

أقسام 100vh وh-screen

القسم الرئيسي المضبوط على 100vh (أو h-screen في Tailwind) يكون بارتفاع النافذة. وعند الطباعة تُحسب وحدات نافذة العرض على مساحة الصفحة، فيحتلّ القسم صفحة كاملة ويترك فراغاً كبيراً. وفي الالتقاط بالصور يأخذ ارتفاع النافذة التي تستخدمها الأداة. أعد ضبط الارتفاع عند الطباعة، أو دع المحوّل يثبّت أقسام 100vh على ارتفاع النافذة الذي اخترته.

@media print {
  .hero, .h-screen { min-height: auto; height: auto; padding: 48px 0; }
}

رؤوس ثابتة أو لاصقة في كل صفحة

يطبع Chrome وEdge العناصر ذات position: fixed في كل صفحة، فيغطّي شريط التنقّل الثابت أعلى كل صفحة. وقد تظهر الرؤوس اللاصقة (sticky) في أماكن غريبة أيضاً. اجعلها static عند الطباعة وأخفِ العناصر التي لا معنى لها على الورق. ويمكن للمحوّل أيضاً إظهار الرؤوس الثابتة واللاصقة مرة واحدة في الأعلى بدلاً من تكرارها.

@media print {
  header, .navbar { position: static; }
  .cookie-banner, .chat-widget, .back-to-top { display: none; }
}

الخلفية الداكنة صارت بيضاء

توفيراً للحبر، تحذف المتصفحات ألوان الخلفية وصورها عند الطباعة ما لم يُطلب غير ذلك. فتتحوّل لوحة تحكّم داكنة إلى نص أبيض على صفحة بيضاء. هناك حلّان: تفعيل خيار رسومات الخلفية في نافذة الطباعة (ضمن المزيد من الإعدادات في Chrome وEdge)، أو التصريح في CSS بوجوب الحفاظ على الألوان كما هي. والخاصية موروثة، فوضعها على html يشمل الصفحة كلها.

html {
  -webkit-print-color-adjust: exact; /* Safari and older Chromium */
  print-color-adjust: exact;
}

نصوص وصور وصفوف جداول مقطوعة عند فواصل الصفحات

يقطع محرّك الطباعة الصفحة حيثما يصل المحتوى إلى أسفل المساحة القابلة للطباعة، حتى لو كان ذلك في منتصف سطر أو صورة أو بطاقة. أخبره بالعناصر التي يجب أن تبقى كاملة، وأين تبدأ صفحات جديدة، وكم سطراً يمكن أن يبقى منفرداً في أعلى الصفحة أو أسفلها.

figure, img, tr, pre, blockquote, .card { break-inside: avoid; }
h2, h3 { break-after: avoid; }
.chapter { break-before: page; }
p { orphans: 3; widows: 3; }
thead { display: table-header-group; } /* repeat table headers on each page */

صور مفقودة ومحتوى غير مرئي

الصور الكسولة والمحتوى الذي يظهر بالتمرير

الصور ذات loading="lazy" لا تُجلب إلا عند اقترابها من نافذة العرض، ومكتبات مثل AOS تُبقي العناصر عند opacity 0 حتى تمرّر إليها. لذا قد يُظهر التقاط الصفحة كاملة إطارات فارغة وكتلاً بيضاء. استخدم التحميل الفوري (eager) للصور المهمة، وافرض الحالة الظاهرة عند الطباعة، أو دع المحوّل يمرّر الصفحة ويفرض تحميل الصور الكسولة قبل الالتقاط.

<img src="images/team.jpg" alt="Team photo" loading="eager" width="1200" height="800">
@media print {
  [data-aos], .reveal-on-scroll { opacity: 1 !important; transform: none !important; }
}

صور من مواقع أخرى (CORS)

المخرجات المبنية على اللقطات تعيد رسم الصفحة على لوحة canvas. وتمنع المتصفحات قراءة بكسلات الصور القادمة من نطاقات أخرى ما لم يرسل خادمها ترويسات CORS، لذا قد تغيب هذه الصور عن تنزيل PDF أو PNG أو PPTX بينما تظهر سليمة في الطباعة. ضع الصور في ملف ‎.zip بجانب ملف HTML، أو استضفها على خادم يرسل Access-Control-Allow-Origin، أو استخدم الطباعة / الحفظ بصيغة PDF.

حركات ظهور لا تكتمل أبداً

إذا كان العنصر مخفياً في نمطه الأساسي ولا يظهر إلا بالحركة، فإن أي شيء يوقف الحركة (ملف أنماط للطباعة، أو التقاط في لحظة خاطئة، أو إعداد تقليل الحركة) يُبقيه مخفياً. جمّد الحركات عند حالتها النهائية في المحوّل، واكتب الحركات بحيث تكون الحالة المرئية هي الافتراضية.

/* Fragile: invisible unless the animation runs to the end */
.card { opacity: 0; animation: fade 0.5s forwards; }
@keyframes fade { to { opacity: 1; } }

/* Robust: visible by default, only the start is animated */
.card { animation: fade-in 0.5s both; }
@keyframes fade-in { from { opacity: 0; } }

ملف أنماط طباعة جاهز للّصق

يجمع هذا القسم الحلول السابقة. أضفه في نهاية ملف CSS، وكيّف المحدِّدات مع صفحتك، ثم استخدم الطباعة / الحفظ بصيغة PDF. انتبه إلى السطر الأخير: فهو يوقف الحركات، لذا لا يكون آمناً إلا إذا كانت عناصرك المتحرّكة ظاهرة افتراضياً كما شرحنا أعلاه.

@page { size: A4; margin: 12mm; }

@media print {
  html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  header, .navbar { position: static; }
  nav, .cookie-banner, .chat-widget { display: none; }
  .hero, .h-screen { min-height: auto; height: auto; }
  h2, h3 { break-after: avoid; }
  figure, img, tr, pre, .card { break-inside: avoid; }
  thead { display: table-header-group; }
  [data-aos], .reveal-on-scroll { opacity: 1 !important; transform: none !important; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

أسئلة شائعة

لماذا يبدو ملف HTML في PDF مختلفاً عنه في المتصفح؟

الطباعة / الحفظ بصيغة PDF تطبّق قواعد الطباعة: حذف الخلفيات افتراضياً، وتكرار العناصر الثابتة، وفواصل صفحات داخل المحتوى. أما ملفات PDF المبنية على اللقطات فتنسخ الشاشة لكنها قد تلتقط قبل اكتمال الخطوط أو الرسوم أو الحركات. حدّد الطريقة ثم طبّق الحل المناسب.

كيف أحافظ على ألوان الخلفية عند حفظ HTML بصيغة PDF؟

أضف print-color-adjust: exact (مع النسخة ذات البادئة ‎-webkit-‎ لمتصفح Safari) على عنصر html، أو فعّل خيار رسومات الخلفية في نافذة الطباعة. أما ملفات PDF المبنية على اللقطات فتحتفظ بالخلفيات تلقائياً لأنها تنسخ الشاشة.

لماذا تظهر الرسوم البيانية فارغة في ملف PDF؟

كان الرسم ما زال في حركته، أو لم يُرسم بعد، لحظة الالتقاط. اضبط animation: false في Chart.js، وانتظر بضع ثوانٍ قبل الالتقاط، وأعد تحجيم الرسوم عند حدث beforeprint لتناسب عرض الصفحة.

كيف أمنع فاصل الصفحة من قطع صورة أو صف جدول؟

طبّق break-inside: avoid على الصور والأشكال والبطاقات وصفوف الجداول، وdisplay: table-header-group على thead لتكرار رؤوس الجداول. العنصر الأطول من صفحة سيُقسَم رغم ذلك، فاجعل هذه الكتل أقصر من صفحة واحدة.

لماذا لا يمكن تحديد النص في ملف PDF؟

بُني الملف من لقطات للصفحة (مثلاً عبر html2canvas وjsPDF)، فكل صفحة فيه صورة. استخدم بدلاً من ذلك الطباعة / الحفظ بصيغة PDF، إذ يحتفظ محرّك الطباعة في المتصفح بنص حقيقي قابل للتحديد والبحث.

جرّبه: محوّل HTML إلى PDF مجاناً

يعمل داخل متصفحك: بدون رفع، بدون تسجيل، بدون علامة مائية.

أداة مجانية من دورني، منصة الولاء بالألعاب للأنشطة التجارية.