مقال تقني

تصدير أوراق عمل Excel إلى PDF في Delphi بدون Office

تصدير ورقة عمل Excel إلى PDF من Delphi أو C++Builder دون تثبيت Office، استدعِ SaveAsPDF على ورقة عمل أو دفتر عمل HotXLS؛ وإن HotXLS عبارة عن مكون Excel أصلي لـ VCL، ويعرض تصدير ورقة العمل إلى PDF النطاق المستخدم للورقة إلى ملف PDF 1.4 مستقل بذاته، مطابقاً عرض أعمدة Excel ونص عرض الخلايا على صفحات A4 مع ترقيم تلقائي لصفحات متعددة؛ ولا تشارك أي نسخة من Excel، ولا برنامج تشغيل طابعة، ولا مكتبة DLL خارجية في هذا العرض

ويعامل HotXLS ملف الـ PDF كتنسيق لعرض البيانات، وهو شقيق لمسارات تصدير CSV و TSV و HTML الخاصة به بدلاً من كونه محرك عرض دقيق البكسل؛ ويعيد التصدير إنتاج المحتوى والتخطيط الجدوليين: القيم التي تراها في الشبكة، ونسب الأعمدة، وخطوط الشبكة حول الخلايا؛ ويهمنا هذا الإطار لأنه يضع توقعاً صادقاً مقدماً، ويدور بقية هذا المقال حول مكان حدوث ذلك بالضبط

كيف تقوم بتحويل Excel إلى PDF في Delphi بدون Office؟

المسار الأقصر هو استدعاء واحد؛ حيث تعرض TXLSWorksheet الدالتين SaveAsPDF(FileName) و SaveAsPDF(Stream)، وتعرض TXLSWorkbook نفس التحميلين الزائدين اللذين يوجهان إلى الورقة النشطة؛ ويرجع كل منها عدداً صحيحاً (Integer)، بقيمة ١ عند النجاح و -1 عند الفشل، بحيث يمكنك التفريع بناءً على النتيجة دون تغليف الاستدعاء في معالجة استثناءات خاصة بك

uses
  lxHandle;

var
  Book: TXLSWorkbook;
begin
  Book := TXLSWorkbook.Create;
  try
    Book.Open('sales-june.xlsx');
    // عرض النطاق المستخدم للورقة النشطة مباشرة إلى report.pdf.
    if Book.SaveAsPDF('report.pdf') = 1 then
      Writeln('PDF written');
  finally
    Book.Free;
  end;
end;

عند استدعاء SaveAsPDF على ورقة عمل، يصدر HotXLS الـ UsedRange لتلك الورقة، وهو المربع المحيط بكل خلية مأهولة؛ والتحميل الزائد لدفتر العمل هو وسيلة مريحة تفوض العمل للورقة النشطة، ولذلك فإن التقرير متعدد الأوراق إما يمر في حلقة فوق الأوراق التي يحتاجها أو يختار الورقة النشطة قبل الاستدعاء؛ ولا يوجد وضع لدفتر عمل كامل، أو وضع الكل في مرور واحد يربط كل تبويب: فكل استدعاء يعرض ورقة واحدة أو نطاقاً واحداً

كيف يحول HotXLS النطاق المستخدم إلى صفحات PDF؟

يمر HotXLS عبر النطاق المستخدم خلية بخلية ويخططه كشبكة مرقمة الصفحات؛ وتأتي هندسة الأعمدة مباشرة من Excel: حيث يتم تحويل عرض كل عمود من وحدة عرض Excel إلى النقاط الطباعية، مع وجود حد أدنى حتى لا ينهار أي عمود إلى شريحة صغيرة؛ وعندما تكون الأعمدة مجتمعة أعرض من الصفحة الصالحة للاستخدام، يقوم HotXLS بتقليصها بشكل موحد لتناسب الصفحة; وعندما تكون مناسبة بالفعل، يتركها بعرضها الطبيعي بدلاً من تمديدها عبر الورقة

ثم تتدفق الصفوف إلى أسفل الصفحة حتى يتجاوز الصف التالي الهامش السفلي، وعند هذه النقطة يفتح HotXLS صفحة جديدة ويستمر، وبذلك تترقم صفحة ذات ألف صف من تلقاء نفسها دون أي دفترة لفواصل الصفحات منك؛ وتطبع كل خلية الـ FormattedText الخاص بها، وهو سلسلة العرض المنتجة بعد تطبيق تنسيق الأرقام، وبذلك يظهر التاريخ كما تظهره الورقة وتحتفظ خلية العملة بفاصل الآلاف ورمزها؛ ويتم قص النص الذي يتجاوز عموده إلى عرض الخلية بدلاً من انسكابه في الخلية المجاورة

التحكم في الاتجاه والهوامش وحجم الخط

إن TXLSPDFExport هي الفئة الكامنة وراء المساعدين ذوي السطر الواحد، وتنشر ثلاث خصائص: Orientation (وهو xlsPdfPortrait أو xlsPdfLandscape، والافتراضي عمودي)، و Margin (بالنقاط، والافتراضي ٤٠)، و FontSize (بالنقاط، والافتراضي ٩)؛ واضبطها قبل استدعاء SaveAsPDF ويلتقطها المستند بأكمله

uses
  lxHandle, lxPDF;

var
  Book: TXLSWorkbook;
  Exporter: TXLSPDFExport;
begin
  Book := TXLSWorkbook.Create;
  try
    Book.Open('wide-ledger.xlsx');
    Exporter := TXLSPDFExport.Create;
    try
      Exporter.Orientation := xlsPdfLandscape;  // ملاءمة الأوراق العريضة في الصفحة
      Exporter.Margin := 28;                     // هامش صفحة أضيق، بالنقاط
      Exporter.FontSize := 8;                    // صفوف أكثر كثافة
      Exporter.SaveAsPDF(Book.ActiveSheet, 'ledger.pdf');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

وتقبل TXLSPDFExport أيضاً نطاقاً بدلاً من ورقة كاملة من خلال SaveAsPDF(Range: IXLSRange; ...)، وهي كيفية تصدير كتلة ملخص من ورقة تحتوي أيضاً على حسابات مسودة؛ والتحميل الزائد للنطاق هو نفسه الذي يعتمد عليه مساعد ورقة العمل داخلياً، ولذلك يتصرف التخطيط والقياس وترقيم الصفحات بشكل متطابق سواء سلمته نطاقاً مستخدماً كاملاً أو تحديداً منتقى يدوياً

var
  Book: TXLSWorkbook;
  Exporter: TXLSPDFExport;
  Summary: IXLSRange;
begin
  Book := TXLSWorkbook.Create;
  try
    Book.Open('quarter.xlsx');
    // تصدير كتلة الملخص فقط، وليس أعمدة المسودة بجوارها.
    Summary := Book.Sheets[1].Range['A1', 'F20'];
    Exporter := TXLSPDFExport.Create;
    try
      Exporter.SaveAsPDF(Summary, 'summary.pdf');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

لماذا لا يدمج HotXLS أي خط: المفاضلة بين Helvetica و WinAnsi

يكتب HotXLS النص بخط Helvetica المدمج في PDF وترميز WinAnsiEncoding، وهو اختيار متعمد بدون تبعيات مع حد حقيقي واحد؛ ولأن خط Helvetica هو أحد الخطوط الـ ١٤ القياسية التي يحملها كل قارئ متوافق مع PDF بالفعل، لا يدمج المصدر أي برنامج خطوط على الإطلاق: ويظل الملف صغيراً ويفتح في أي مكان، دون الحاجة لترخيص شيء أو شحنه بجواره؛ وتكلفة هذا الاختيار هي مجموعة الأحرف التي يمكن للصفحة إظهارها

يغطي ترميز WinAnsiEncoding مجموعة Windows-1252، ولذلك يعرض النص اللاتيني وعلامات الترقيم الشائعة واللكنات الأوروبية الغربية المعتادة بشكل صحيح، ويطوي HotXLS حفنة من الأحرف الطباعية وعلامات الاقتباس الملتوية والشرطات القصيرة والطويلة وعلامة اليورو في فتحات CP1252 الخاصة بها؛ وتنهار الأحرف خارج هذا النطاق، كرموز CJK الرسومية والروسية واليونانية والعربية، إلى علامة استفهام على الصفحة؛ وإذا كانت أوراقك تحمل نصوصاً غير لاتينية، فإن هذا المصدر هو الأداة الخاطئة، وعارض الـ PDF كامل التنسيق هو العمل الذي يناسبك

أين يتوقف تصدير ورقة العمل إلى PDF

يعرض HotXLS المحتوى والهيكل، وليس النمط المرئي الكامل للورقة؛ حيث تقع تعبئة الخلايا، وأنماط الحدود لكل حافة، وخطوط وألوان كل خلية، والمخططات البيانية والصور المدمجة خارج نطاق ما يرسمه المصدر؛ وما تحصل عليه هو شبكة النص مع خطوط شبكة موحدة؛ وهذا هو نفس العقد الذي تحترمه مصادر HTML و RTF، وهو الخيار الصحيح لتقرير أو تسليم بيانات حيث تحمل الأرقام المعنى وليس المظهر

وعندما يكون الهدف مستنداً منسقاً ومتسقاً مع العلامة التجارية، فإن النمط المعتاد هو تجميع الأرقام في قالب جدول بيانات أولاً، وهو ما يغطيه سير عمل إنشاء التقارير القائم على القوالب بالتفصيل؛ ولأن شيئاً من هذا لا يلمس COM أو نسخة قيد التشغيل من Excel، فإن خط الأنابيب يناسب نفس نموذج الأتمتة الخالي من Office الذي يستخدمه HotXLS طوال الوقت: وهو Pascal نقي، وقابل للنشر على خادم بدون واجهة رسومية، مع عدم وجود تشغيل بيني لترخيصه أو مراقبته

ملف الـ PDF المستقل بذاته، بايتًا ببايت

يبني HotXLS هيكل ملف الـ PDF يدوياً ويحسب جدول الإسناد ترافقي (cross-reference table) الخاص به من إزاحات البايتات الدقيقة، وهو السبب في فتح المخرجات في كل قارئ؛ وأثتاء تسلسل كل كائن، يسجل HotXLS إزاحة البداية لذلك الكائن قبل إلحاق بايتاته، ثم يقدم الموضع الجاري بطول ما كتبه للتو، بحيث يهبط جدول xref بدقة على كل كائن وتحدد startxref أين يبدأ ذلك الجدول؛ والقارئ الذي يجد تلك الإزاحات منحرفة ولو ببايت واحد يرفض الملف بأكمله، مما يجعل الإزاحة المحسوبة ذاتياً هي العقد الأساسي للمصدر

ويحمل هذا العقد نتيجة صارمة واحدة تستحق المعرفة: تُكتب عقدة الصفحات (Pages) أولاً ككائن ١، ولكن قائمة الصفحات التابعة لها غير معروفة حتى يتم إصدار كل صفحة، ولذلك يحجزها HotXLS كعنصر نائب ويرقع الصفحات التابعة والعدد النهائيين قبل تشغيل مرور الإزاحة، وليس بعده أبداً؛ ومجرد قياس بايتات كائن ما للـ xref، يتم تجميدها: فمن شأن تغيير بايت واحد بعد ذلك أن يزيح كل إزاحة تالية ويفسد الجدول؛ وهو انضباط صغير يشتري مستند PDF 1.4 صالحاً للمعايير دون أي مكتبة تحته

يتم شحن طرق SaveAsPDF وفئة TXLSPDFExport الموضحة هنا مع مكون HotXLS لـ Delphi Excel، إلى جانب مصادر CSV و HTML و RTF وواجهة برمجة تطبيقات ورقة العمل الكاملة لـ Delphi و C++Builder