مقال تقني

تصدير نطاق خلايا Excel كصورة واحدة مع HotXLS

أحيانا يكون التسليم ليس مستندا بل صورة لجدول. كتلة ملخص في بريد حالة، أو لوحة مؤشرات أداء مصيّرة في لوحة معلومات، أو مصغرة بجانب نتيجة بحث: كلها تريد الخلايا ولا واحدة منها تريد ورقا. يأخذ TXLSCellImageExporter في HotXLS مستطيل خلايا كلاسيكيا أو XLSX وينتج صورة PNG أو JPEG واحدة مضغوطة بلا حجم صفحة ولا هوامش ولا ترويسات أو تذييلات ولا عناوين طباعة ولا فواصل صفحات. والدقة والمقياس والصيغة وجودة JPEG قابلة للضبط، والكائنات وخطوط الشبكة وحدود الخلايا لها مفاتيح مستقلة، ويمكن أن تكون الخلفية لونا أو شفافة، ويجري كتابة الملف عبر استبدال ذري في المجلد نفسه يترك الهدف القائم دون مساس إذا فشل أي شيء

سبب احتياج هذا إلى مصدر تصدير خاص به بدل علم على مسار الطباعة أن الترقيم ليس طبقة اختيارية يمكنك إطفاؤها. إنه الشيء الذي وُجد مسار الصفحة ليفعله

لماذا لا تصير النطاق عبر مسار الطباعة؟

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

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

يقيس TXLSCellImageExporter ويرسم ويرمز صورة واحدة لكل نطاق خلايا بينما يقسم مسار الطباعة النطاق عند فواصل الصفحات
مسار الصفحة يدخل هندسة ورق بينك وبين الخلايا؛ ومصدر تصدير الخلايا لا صفحة له في أي مكان في المسار

قس قبل أن تصير

تعيد Measure الأبعاد البكسلية التي ستنتجها الإعدادات الحالية دون ترميز شيء. وهذا مهم لسببين. قالب HTML أو بريد يحتاج عادة أبعاد الصورة قبل وجود الصورة كي يحجز الصندوق ويتجنب إزاحة التخطيط. وخدمة تصير نطاقات اختارها المستخدم تحتاج طريقة لرفض طلب سخيف قبل التخصيص له

uses
  lxHandleX, lxPagination;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Exporter: TXLSCellImageExporter;
  Summary: TXLSXRange;
  W, H, Bytes: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarter.xlsx');
    Sheet := Book.Sheets.ByPos[0];
    Summary := Sheet.Range['A1:F20'];

    Exporter := TXLSCellImageExporter.Create;
    try
      Exporter.ImageFormat := xpifPng;   // PNG يحافظ على حدة الخطوط الرقيقة
      Exporter.DPI := 96;
      Exporter.Scale := 2.0;             // خرج بكثافة ريتينا
      Exporter.IncludeGridlines := False;
      Exporter.IncludeCellBorders := True;
      Exporter.TransparentBackground := True;
      Exporter.MaxPixels := 40 * 1000 * 1000;
      Exporter.MaxBytes := 8 * 1024 * 1024;

      if not Exporter.Measure(Summary, W, H) then
        raise Exception.Create('range exceeds the configured budget');
      // W وH معروفان الآن؛ احجز صندوق التخطيط قبل الترميز
      Bytes := Exporter.Save(Summary, 'summary.png');
      if Bytes <= 0 then
        raise Exception.Create('image export failed, previous file kept');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

ميزانيات، لأن المقياس يضاعف

MaxPixels وMaxBytes ليستا زينة دفاعية. عدد البكسلات ينمو بمربع معامل المقياس وبمربع نسبة الدقة، فالنطاق الذي هو 1200 في 800 معقول عند 96 DPI يصبح نحو 47 ميغابكسل عند 600 DPI، والمستخدم الذي يختار نطاقا مستخدما كاملا بدل كتلة ملخص يضيف رتبة مقدار أخرى فوق ذلك. ومن دون سقف يكون نمط الإخفاق تخصيصا لا تستطيع العملية تلبيته، وهو ما يسقط كل ما كانت تلك العملية تفعله أيضا

مع سقف يفشل الطلب ويحصل المستدع على الاختيار: رفض، أو خفض المقياس، أو تضييق النطاق. وذلك موضع أفضل بكثير لخادم تقارير، وهو الاستدلال نفسه وراء الميزانيات الصريحة في مفكك الملفات التعريفية الموصوف في مقال مفكك EMF وWMF المحدود

مسار الميزانية لـ TXLSCellImageExporter في HotXLS: Measure تعيد حجم البكسل أولا، ثم يقيد MaxPixels وMaxBytes التخصيص وحجم الخرج
يحدث الرفض قبل التخصيص، وفشل ميزانية البايتات يترك الصورة السابقة دون مساس للمستدع

الاستبدال الذري، ولماذا يهم المجلد

Save إلى اسم ملف لا يكتب في الهدف. يكتب ملفا مؤقتا في المجلد نفسه، ويرمز فيه، وعندها فقط يستبدل الهدف. إذا فشل الترميز، أو تجاوزت الميزانية في منتصف الطريق، أو قُتلت العملية، فالصورة السابقة ما تزال هناك وما تزال صالحة. لوحة معلومات تجدد بلاطاتها على جدول لا تعرض إذن قط PNG مبتورا، وهو العرض المعتاد لكتابة ساذجة تفتح الوجهة وتبدأ البث

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

Save في TXLSCellImageExporter يرمز في ملف مؤقت في المجلد نفسه ثم يستبدل الهدف ذريا؛ والإخفاقات تترك الصورة السابقة صالحة
يجب أن يعيش الملف المؤقت بجانب الهدف لأن الاستبدال الذري لا يعمل إلا داخل قرص واحد

أحداث الرسم ترسم على لوحة الرسم الحقيقية

يكشف مصدر تصدير النطاق ومصدر تصدير الصفحات كلاهما أحداث رسم افتتاحية وختامية، ويستلمان سياقا كاملا للقراءة فقط لا مقبض لوحة رسم فقط. يحمل TXLSPagePaintContext لوحة الرسم الحية، وحدود البكسل، وحجم الصفحة بالنقاط، والدقة والمقياس المستخدمين فعلا، ورقم صفحة المستند، ورقم الصفحة داخل الورقة، وعدد الصفحات الكلي، واسم الورقة، وورقة العمل الأصلية بالصيغتين الكلاسيكية وXLSX. ذلك يكفي لرسم علامة مائية تتقاس بشكل صحيح، أو ختم صفحة يعرف موقعه في التشغيلة

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // واع بالمقياس، فيبدو الختم نفسه عند 1x و3x
  AContext.Canvas.Font.Height := Round(-48 * AContext.Scale);
  AContext.Canvas.Font.Color := clSilver;
  AContext.Canvas.Brush.Style := bsClear;
  AContext.Canvas.TextOut(AContext.Bounds.Left + Round(24 * AContext.Scale),
    AContext.Bounds.Top + Round(24 * AContext.Scale), 'DRAFT');
end;

Exporter.AfterPaint := Job.StampDraft;

ثلاثة سلوكيات تستحق الاعتماد عليها. الأحداث تشتغل مرة واحدة بالضبط لكل إطار مصيّر، بما في ذلك كل إطار من TIFF متعدد الصفحات، فالعداد الذي يزيد في المعالج موثوق. وهي تبقى صامتة أثناء القياس، فالمعالج ذو الأثر الجانبي لا يعمل مرتين لمخرج واحد. وإذا رما الحد الافتتاحي استثناء فالحدث الختامي لا يشتغل ولا بايتات صورة جزئية تكتب، فالاستثناء في كود رسمك لا يستطيع إنتاج ملف مختوم نصفيا

اختيار الصيغة

PNG لكل ما يغلب عليه النص. تطبق JPEG تحويلا كتليا ينتج رنينا مرئيا حول الخطوط الرقيقة عالية التباين، وهي بالضبط ما تكون عليه حدود الخلايا والنص الصغير، وتنجو الشوائب عند إعدادات جودة تبدو فيها صورة فوتوغرافية تامة. ويكسب JPEG مكانه عندما تهيمن على النطاق صور فوتوغرافية مضمنة ويهم حجم الملف أكثر من أمانة الحواف. والخلفيات الشفافة تتطلب PNG، لأن JPEG بلا قناة ألفا، فالبلاطة المقصودة للجلوس على سطح ملون اتخذت الاختيار عنك

إن كان نطاقك يحوي خلايا مدمجة، فافحص الخرج مقابل الورقة: المناطق المدمجة تتفاعل مع عرض الأعمدة بطرق تفاجئ الناس، وقواعد التخطيط مشمولة في مقال الخلايا المدمجة وقوالب التقارير. يقرأ HotXLS ويكتب XLS وXLSX وODS وCSV من Delphi وC++Builder دون أي اعتمادية Excel، وسطح مصادر التصدير الكامل موثق في صفحة منتج HotXLS Delphi spreadsheet component