مقال تقني

HotXLS Delphi Component: charts, images, and drawing objects في Delphi

تتعامل المقالة مع المخططات والصور والكائنات الرسومية كبيانات مرتبطة بالأوراق والمراجع والعلاقات، لا كزينة. وتغطي توسع نطاق المخطط مع الصفوف المدرجة، وأبعاد الصور بوحدة EMU، وثبات الارتساء، وألوان theme في XLS، مع فحص الملف في Excel وLibreOffice

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

HotXLS مكتبة Object Pascal أصلية تقرأ وتكتب XLS وXLSX دون أتمتة Excel، وهي تحمل نموذجَي رسم منفصلين لأن الصيغتين تخزّنان الرسوم بشكل مختلف. صيغة BIFF8 ‎.xls‎ تبقي المخططات على أوراقها المخصصة الخاصة بها والأشكال العائمة في تدفق OfficeArt مرفق بورقة العمل. أما صيغة OOXML ‎.xlsx‎ فيمكنها تضمين مخطط داخل الشبكة نفسها، مرسًى على مستطيل خلية، إلى جانب نفس نوع الصور والأشكال العائمة. نموذج الكائن يعكس ذلك الانقسام، والإخفاقات التي تستحق الكتابة عنها كلها تأتي من تطبيق قواعد صيغة واحدة على الأخرى

مخطط يقارن حاويات الرسم في HotXLS من Delphi: أوراق المخططات وأشكال OfficeArt في XLS القديم مقابل المخططات والصور وصناديق النص المضمنة في XLSX
تكشف صيغتا الملف واجهتَي رسم مختلفتين، لذا يجب اختيار الوعاء قبل كتابة أي كود مخططات

أي حاوية يمكنها احتواء ماذا

اختيار الحاوية يجب أن يسبق أي كود مخططات، لأن أنواع الكائنات المتاحة تختلف بين الاثنتين:

  • XLS (BIFF8): المخططات تعيش على أوراق مخططات مخصصة تُنشأ عبر AddChartSheet على مجموعة Sheets. الصور، ومربعات النص، والمستطيلات، والأشكال البيضاوية، والخطوط هي أشكال OfficeArt تُدار عبر مجموعة Shapes الخاصة بورقة العمل. لا توجد واجهة لتضمين مخطط داخل شبكة ورقة عمل عادية
  • XLSX (OOXML): يمكن تضمين المخططات مباشرة في ورقة عمل بـTXLSXWorksheet.AddChart، مرساة على مستطيل خلية، أو وضعها على ورقة مخططات مخصصة بـTXLSXWorkbook.AddChartSheet. الصور تدخل بـAddImage أو AddImageFromFile، والتسميات العائمة بـAddTextBox

فمتطلب صيغ كـ"ورقة لوحة معلومات مع المخطط بجانب الأرقام" هو في الواقع متطلب لـ‎.xlsx‎. تستطيع تقريبه في ‎.xls‎ فقط بدفع المخطط إلى ورقته الخاصة، وهذا يغيّر كيفية تنقل المستخدم في الملف ويغيّر كيف يجب أن يتصرف كودك. الورقة التي تعيدها AddChartSheet في جهة XLS تدفق فرعي لمخطط، لا شبكة: الكتابة إليها بـCells.Item تنتج تدفق رسم غير متسق يتولّد دون خطأ ويتجاهله Excel لاحقًا عند الفتح. المخطط يختفي ببساطة، ولا شيء في سجل البناء يقول لماذا. عامل الورقة المُعادة كمخصصة للمخططات فقط وستختفي فئة تقارير "المخطط المفقود" بأكملها

تضمين مخطط في ورقة عمل XLSX

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

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Chart: TXLSXChart;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Sheet.Cells[1, 1].Value := 'Region';
    Sheet.Cells[1, 2].Value := 'Revenue';
    Sheet.Cells[2, 1].Value := 'East';
    Sheet.Cells[2, 2].Value := 1184350;
    Sheet.Cells[3, 1].Value := 'Central';
    Sheet.Cells[3, 2].Value := 902210;
    Sheet.Cells[4, 1].Value := 'West';
    Sheet.Cells[4, 2].Value := 1010675;

    // إطار مرسًى على الصفوف 6..22، الأعمدة 1..8
    Chart := Sheet.AddChart(xlsxChartColumn, 'Revenue by Region', 6, 1, 22, 8);
    Chart.AddSeries('Revenue', 'Sales!$A$2:$A$4', 'Sales!$B$2:$B$4');
    Chart.ValueAxisTitle := 'USD';

    Sheet.AddImageFromFile(1, 5, 'logo.png');
    Book.SaveAs('dashboard.xlsx');
  finally
    Book.Free;
  end;
end;

المعامل الذي يعضّ هو سلسلة النطاق المسلَّمة إلى AddSeries. إنها سلسلة حرفية، تُلتقط في لحظة الاستدعاء، ولا تعرف شيئًا عن أنك قد تلحق عشرين صفًا إضافيًا من البيانات لاحقًا. ابنها من عدد صفوف حسبته بعد كتابة البيانات، لا قبلها أبدًا. مخططات التشتت والفقاعات تُثقل نفس المعاملين بمعانٍ مختلفة: نطاق الفئات يزوّد الآن قيم X ونطاق القيم يزوّد Y، ونصف قطر الفقاعة يأتي من مرجع ثالث يُضبط عبر BubbleSizeRange على TXLSXChartSeries المُعادة. اقرأ الاستدعاء كـ"X، Y، الحجم" بدلًا من "الفئات، القيم" بمجرد أن تغادر عائلة الأعمدة والأشرطة

TXLSXChartType يمتد ليشمل مخططات الأعمدة، والأشرطة، والخطوط، والدائرية، والمساحية، والحلقية، والتشتت، والفقاعية، والراداري، وهذا يغطي المستودع اليومي للتقارير. لمخطط بحجم صفحة كاملة دون شبكة محيطة، تعيد Book.AddChartSheet ورقة خاصية IsChartSheet فيها true. إنها نظيرة ‎.xlsx‎ لورقة المخطط القديمة وتحمل نفس التوقع: لا تكتب محتوى خلايا إليها

الصور تدخل كبايتات، وتُقاس بوحدات EMU

هناك حمولتان زائدتان لإدراج صورة، وخلطهما هو خطأ الصور الذي يظهر أكثر من غيره في مراجعة الكود. AddImage(ARow, ACol, AData, AFormat) تريد في AData بايتات الصورة المُرمَّزة بالفعل: المحتوى الخام لملف PNG أو JPEG أو GIF أو BMP. مرّر إليها مسار ملف وستكون قد خزّنت سلسلة من أربعين بايتًا لا يستطيع أي عارض فك ترميزها، وهذا بالضبط تقرير أيقونة الصورة المكسورة الذي لا تريد تصحيحه بعد النشر. عندما يكون المصدر ملفًا على القرص، استدع AddImageFromFile بدلًا من ذلك ودع المكتبة تقرأ البايتات وتصنّف الصيغة نيابة عنك

مخطط لنظامي الإحداثيات خلف TXLSXWorksheet.AddChart في HotXLS: إطار المخطط مرساة إلى صفوف وأعمدة ورقة العمل بينما تستخدم بيانات سلاسله مراجع A1 مطلقة
يثبّت الإطار إلى الصفوف والأعمدة بينما يقرأ الرسم مراجع A1 المطلقة، ولا يعلم أيُّ نظامي الإحداثيات بالآخر

ثم يأتي التحجيم. لا تقيس DrawingML بالبكسل؛ إنها تقيس بوحدات EMU المترية الإنجليزية، حيث تشكّل 914400 وحدة EMU بوصة واحدة، وعند 96 DPI، تشكّل 9525 وحدة EMU بكسلًا واحدًا. كائن TXLSXImage يكشف WidthEMU وHeightEMU، فشعار يُراد له أن يُعرَض بـ180 في 60 بكسلًا يحتاج 1714500 في 571500 وحدة EMU. ضع ذلك التحويل في ثابت مسمّى واحسب مقابله. أرقام سحرية مثل 1714500 متناثرة عبر الكود غير قابلة للقراءة وخاطئة بهدوء أول مرة يغيّر فيها أحدهم DPI الهدف. صف وعمود المرساة، بالمناسبة، يبدآن من 1، مطابقين لبقية واجهة الخلايا لا حساب EMU الذي يبدأ من 0

أوراق المخططات والأشكال في ملفات XLS القديمة

في جهة BIFF8، حمولة AddChartSheet الأغنى الزائدة تأخذ نوع المخطط، وعناوين المحاور، ومصفوفة مفتوحة من سجلات TXLSChartSeriesInfo، حيث يحمل كل سجل اسمًا ونطاق فئات ونطاق قيم كسلاسل نصية. الأشكال العائمة أمر منفصل: إنها تذهب إلى ورقة البيانات نفسها، عبر مجموعة Shapes الخاصة بها، لا إلى ورقة المخطط

var
  Book: IXLSWorkbook;
  Data, Trend: IXLSWorksheet;
  Series: array[0..0] of TXLSChartSeriesInfo;
begin
  Book := TXLSWorkbook.Create;   // معدود بالواجهة: لا تحرره
  Data := Book.Sheets.Add;
  Data.Name := 'Data';
  Data.Cells.Item[1, 1].Value := 'Month';
  Data.Cells.Item[1, 2].Value := 'Units';
  Data.Cells.Item[2, 1].Value := 'Apr';
  Data.Cells.Item[2, 2].Value := 1530;
  Data.Cells.Item[3, 1].Value := 'May';
  Data.Cells.Item[3, 2].Value := 1721;

  Series[0].Name := 'Units';
  Series[0].Categories := 'Data!$A$2:$A$3';
  Series[0].Values := 'Data!$B$2:$B$3';
  Trend := Book.Sheets.AddChartSheet('Trend', xlsChartTypeLine,
    'Units sold', 'Month', 'Units', Series);
  // Trend تدفق فرعي لمخطط: لا تستدع أبدًا طرق خلايا عليها

  Data.Shapes.AddTextBox('Source: ERP nightly export', 6, 1, 8, 4);
  Data.Shapes.AddPicture('approved-stamp.bmp');
  Book.SaveAs('trend.xls');
end;

تفصيلا دورة حياة يهمّان هنا، ويسحبان في اتجاهين متعاكسين. TXLSWorkbook تُحمل عبر واجهة IXLSWorkbook وهي معدودة مرجعيًا، فاستدعاء Free عليها بنفسك يثير تحريرًا مزدوجًا. TXLSXWorkbook من الأقسام السابقة كائن عادي ويجب تحريره في try..finally. نفس مراجع الكود الذي يشير إلى Free مفقودة في جهة XLSX عليه أن يشير إلى وجودها في جهة XLS، وهذا خطر تعثّر حقيقي عندما تعمل في كلا الصيغتين في نفس الوحدة. مساعدات الأشكال نفسها موحّدة: AddRectangle وAddOval وAddLine، مع DeleteInRange لمسح منطقة من الرسوم، كلها ترسو بأزواج صف وعمود، فقالب يُدرج صفوفًا فوقها يزيحها مع الشبكة

خاصية أخرى تكسب مكانتها في الملفات القديمة. TXLSPicture.TransparentColor تخفي لونًا خلفيًا مختارًا من صورة نقطية، وهذه هي الطريقة التي تُسقط بها ختمًا غير مستطيل (خاتم "معتمد"، علامة مائية) فوق الشبكة في صيغة لم يتعلم عرض BIFF الخاص بها أبدًا قناة ألفا لـPNG. اضبط اللون الذي أُلِّف الختم مقابله وسيختفي المستطيل المحيط

مخطط لإدراج الصور في HotXLS من Delphi: تريد AddImage بايتات مرمّزة بينما تقرأ AddImageFromFile الملف، وتتحول بكسلات 96 DPI إلى قيم WidthEMU و HeightEMU
بايتات الصورة ومسارات الملفات ينتميان إلى أحمال متعددة مختلفة، وتتحول المقاسات البكسلية على الشاشة إلى EMUs قبل بلوغها كائن الصورة

ألوان السمات لا تنجو من رحلة ذهاب وإياب عبر BIFF8

تعبئات رسم OOXML يمكنها الإشارة إلى فتحة لون سمة، وهذا سبب رخص إعادة تلوين ‎.xlsx‎ بأكمله باستبدال سمته. سجلات رسم BIFF8 ليس لديها فتحة كهذه. عندما يطبّق HotXLS لون سمة على رسم XLS، فإنه يحلّ اللون إلى قيمة RGB حرفية ويخزّنها؛ فهرس السمة الذي جاء منه يختفي في اللحظة التي يُكتب فيها الملف، ولا يمكن استعادته عند إعادة الفتح. هذا يوقع تحديدًا أدوات التقارير ذات العلامة البيضاء، من النوع الذي يعيد تسمية علامة نفس المستند المولَّد لعدة عملاء. أبقِ خريطة السمة إلى RGB في إعداداتك الخاصة وأعد تطبيقها في كل مرة تولّد، بدلًا من توقع قراءتها مجددًا من ملف ‎.xls‎ محفوظ

قرار ذو صلة يظهر في جهة الأداء. يمكن توجيه واجهة XLS لتخطي تحليل طبقة الرسم بأكملها عندما يكون كل ما تريده من ملف قديم كبير هو بيانات خلاياه، بضبط _DisableGraphics إلى true، وهذا يوفر وقتًا حقيقيًا في القراءات الجماعية. المقابل دائم: مصنف فُتح بتلك الطريقة ليس لديه تدفق OfficeArt في الذاكرة، فحفظه يكتب الرسوم خارج الوجود. احتفظ بتلك العلامة لمهام التحليلات للقراءة فقط. الصورة الأوسع للأداء موجودة في ملاحظاتنا عن أداء المصنفات الكبيرة في HotXLS

إبقاء المراسي مستقرة بينما تتغير الشبكة

نادرًا ما تبقى التقارير بالحجم الذي وُلِّدت به، وهنا يؤتي نموذج المرساة من الافتتاحية ثماره. العمليات البنيوية لواجهة XLSX (InsertRows وDeleteRows ومكافئات الأعمدة) تحرّك الطبقات التابعة مع الخلايا. المناطق المدمجة، والروابط التشعبية، والتعليقات، واللوحات المجمّدة، ونطاقات التصفية، والتنسيقات الشرطية، والتحقق من الصحة، والجداول، والأسماء المعرَّفة، ولهذا الموضوع، مراسي الصور والمخططات، كلها تسافر معًا. شعار مرسًى عند الصف 1 يبقى في الأعلى عندما تدخل عشرة صفوف أسفله. إطار مخطط مرسًى تحت كتلة البيانات ينزلق للأسفل مع نمو الكتلة. الشيء الوحيد الذي لا يُعاد كتابته هو أي سلسلة نطاق التقطتها كسلسلة حرفية قبل حدوث الإدراج، لأنها مجرد نص لا سبب للمكتبة لإعادة زيارته. هذا يثبّت الترتيب الآمن لتعبئة قالب: اكتب البيانات وشكّلها أولًا، وأنشئ المخططات وضع الصور كالتمريرة الأخيرة، مع اشتقاق كل سلسلة نطاق من أعداد الصفوف التي لديك بعد الإدراجات، لا قبلها

أداتان أصغر تكملان عدة التموضع. TXLSTextBox.SetArea في جهة XLS تعيد إرساء مربع نص موجود أو شكل تلقائي على مستطيل خلية جديد، وهذا أفضل من حذفه وإعادة إنشائه عندما ينزاح شريط تذييل. أما الحمولة الزائدة من نوع bitmap لـAddPicture فتأخذ TBitmap حيًا مع علامة شفافية اختيارية، فأي شيء يستطيع كود VCL الخاص بك رسمه (مقياس، شريط sparkline، نوع مخطط لا تقدمه القائمة الأصلية) يمكن ختمه مباشرة داخل الورقة دون كتابة ملف مؤقت أولًا

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