خلفية تقارير Delphi أصدرت ملفات .xlsx لسنوات تواجه متطلبًا جديدًا: قواعد المشتريات لدى عميل من القطاع العام تفرض خرج OpenDocument Spreadsheet، والمحللون في ذلك الحساب يعيدون تعديلاتهم كملفات .ods محفوظة من LibreOffice. فالآن يجب على الكود نفسه أن يكتب ODS وأن يقرأه أيضًا. HotXLS، مكتبة جداول بيانات Object Pascal الأصلية من losLab لـDelphi وC++Builder، تتولى الاتجاهين دون أي تثبيت لـExcel أو LibreOffice في أي مكان. الاتجاهان متقاربان لكنهما ليسا مطابقين. فمنذ HotXLS 2.88.229 يعيد مسار الاستيراد الصيغ كصيغ حية جنبًا إلى جنب مع نتائجها المخبأة، كما تقرأ الإصدارات الحالية الأنماط وتنسيقات الأرقام والنطاقات المدمجة والتعليقات والصور والمخططات والتنسيقات الشرطية. وما زال لا يصمد هو دلالات OpenFormula الكاملة وحفنة من تفاصيل الحزمة، لذا فالفريق الذي يفترض مرآة كاملة سيظل يصطاد حالات حدية في مكان ما بين مراجعة العميل والتقرير التالي، غالبًا من دون أي خطأ يشير إليه
دعم ODS يعيش في واجهة XLSX، لا في واجهة XLS
يشحن HotXLS تسلسلين هرميين مستقلين للأصناف في حزمة واحدة: TXLSWorkbook في وحدة lxHandle لملفات .xls الثنائية بصيغة BIFF8، وTXLSXWorkbook في وحدة lxHandleX لحزم OOXML بصيغة .xlsx. كل نقطة دخول لـOpenDocument - OpenODS وSaveAsODS وGetODSSheetNames - تتعلق بـTXLSXWorkbook. هذا الموضع ليس اعتباطيًا. حزمة ODS، كما تحددها مواصفة OASIS ODF 1.3، هي أرشيف مضغوط يحمل عضو mimetype وبيانًا وجسم content.xml، وهذا يجعلها ابنة عم بنيوية لأرشيف OOXML؛ أما BIFF8 فهو تدفق سجلات ثنائي من تسعينيات القرن الماضي لا يشترك في شيء معها
لذلك الموضع حافة عملية: مصنف .xls قديم لا يمكن أن يصبح .ods باستدعاء واحد. تبني جسرًا لمحتوى BIFF إلى نموذج XLSX أولًا، عبر SaveXLSWorkbookAsXLSX من وحدة lxXlsxExport، ثم تعيد فتح الناتج عبر TXLSXWorkbook، ثم تصدّر من هناك. الجسر ليس عديم الفقد، ويستحق معرفة الثغرات قبل البناء عليه. إنه ينسخ القيم والصيغ وتنسيقات الأرقام والخطوط والتعبئات والحدود والنطاقات المدمجة وعروض الأعمدة وارتفاعات الصفوف. ويُسقط التعليقات والمخططات والتنسيق الشرطي. مصدر .xls ذو تنسيق ثقيل سيصل إلى ODS أبسط مظهرًا مما غادر، وهذه خاصية للجسر، لا لكاتب ODS
الكشف في جهة الاستيراد تلقائي. طريقة Open العادية تتعرف على حزمة ODS من عضوها mimetype، وتلجأ إلى فحص content.xml في المستوى الأعلى عندما يكون ذلك العضو غائبًا، لذا فإن مسار كود عام من نوع "افتح أيًا كان ما رفعه المستخدم" لا يحتاج أي استنشاق امتداد خاص به. بعد الفتح، خاصية SourceFormat تُبلغ أي فرع تم تفعيله
التصدير إلى ODS باستخدام TODSExportOptions
استدعاء التصدير نفسه سطر واحد؛ أما كائن الخيارات المحيط به فيحمل القرارات التي سيسأل عنها مراجع لاحقًا:
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // المستدعي يملك هذا الكائن ويحرره
try
Opts.Generator := 'ReportService 4.2'; // تجاوز meta:generator
Opts.IncludeCharts := True;
Opts.IncludeImages := True;
Book.SaveAsODS('quarterly-report.ods', Opts);
finally
Opts.Free;
end;
finally
Book.Free;
end;
end;
المستدعي يملك كائن الخيارات. لن يحرره HotXLS، وهذا سبب وجود try..finally الداخلي وعدم كونه اختياريًا. الخاصيتان اللتان تغيّران الخرج فعليًا، لا مجرد وسمه، تستحقان نظرة أقرب. ضبط IncludeCharts := False يفعل أكثر من إخفاء المخططات: إنه يزيل المستندات الفرعية للمخططات وإدخالات البيان الخاصة بها من الحزمة تمامًا، وهذا بالضبط ما تريده عندما يكون المستهلك خط أنابيب بيانات سيتعثر بها. Generator يتجاوز سلسلة meta:generator الخاصة بـODF، التي تقرأ خلاف ذلك HotXLS/<version>؛ تجاوزها عندما تبصم أدوات لاحقة منتجي الملفات لتوجيه الدعم. إذا لم ينطبق شيء من ذلك، تخطَّ كائن الخيارات كليًا. استدعاء SaveAs(FileName, xlsxOpenDocumentSpreadsheet) مطابق لـSaveAsODS بالإعدادات الافتراضية، والحمولات الزائدة من نوع تدفق في كليهما تتيح لك كتابة الحزمة مباشرة إلى استجابة HTTP دون ملف مؤقت
ما الذي يقرؤه مسار الاستيراد - وما الذي لا يزال يتخطاه
اقرأ هذا الجزء بعناية قبل أن تعِد أي أحد بدقة رحلة ذهاب وإياب. بدأ استيراد ODS في HotXLS مسارًا خفيفًا يحتفظ بالقيم العددية ونتائج الصيغ المخبأة فقط، وقد نما منذ ذلك أبعد بكثير. الإصدارات الحالية تقرأ content.xml مع styles.xml وsettings.xml: القيم العددية ونتائج الصيغ المخبأة، والصفوف والأعمدة المتكررة موسّعة إلى الشبكة، والنطاقات المدمجة، والنص الغني، والروابط التشعبية، والتعليقات، والصور والمخططات المضمّنة، وأنماط الخلايا الموروثة والافتراضية، وتنسيقات الأرقام والتواريخ، وارتفاعات الصفوف وعروض الأعمدة، والنطاقات المسماة، وتثبيت الأجزاء، ونطاقات AutoFilter، والتحقق من صحة البيانات، والتنسيقات الشرطية. وما OpenODS لا يزال لا يحوّله هو دلالات OpenFormula الكاملة، ومفاتيح حماية ODS، وهندسة التعليقات، والتنسيق المتقدم للتعليقات، وميزات الحزمة الواقعة خارج محتوى جدول البيانات
الصيغ هي الجزء الأرجح أن يعضّك. خلية ODF تخزن شيئين جنبًا إلى جنب: تعبير الصيغة، مكتوبًا بلهجة OpenFormula المعرَّفة في ODF 1.3 الجزء 4، وآخر قيمة حسبها التطبيق المُنتِج لها. الإصدارات المبكرة كانت تقرأ القيمة المخبأة فقط. ومنذ HotXLS 2.88.229 يستورد OpenODS التعبير أيضًا: الخلية المكتوبة بالبادئة of:= أو oooc:= تحتفظ بقيمتها المخبأة وتحصل على صيغة Excel مطبَّعة، مع تحويل المراجع المعقوصة مثل [.A1:.B1] وفواصل الوسائط المنقوطة إلى صياغة Excel. ومنذ 2.384.32 يحوّل الاستيراد أيضًا صفوف المصفوفات المضمّنة ({1;2|3;4} تصبح {1,2;3,4}) وعامل اتحاد المراجع ~، ومنذ 2.384.55 يُقرأ ذلك الاتحاد من دون زوج أقواس ثانٍ. تبقى ترجمة OpenFormula إلى صياغة Excel مشكلة تحويل لهجة بحالات حدّية حقيقية حول مفردات الدوال وصياغة المراجع ونماذج الأخطاء، ولهذا تأتي القيمة المخبأة على الدوام: فالأرقام التي تستوردها هي بالضبط الأرقام التي رآها المرسل آخر مرة
ضاق نمط الفشل الذي يجب التصميم حوله تبعًا لذلك. قبل 2.88.229 كان جدول بيانات كانت مجاميعه صحيحة عندما حفظه LibreOffice آخر مرة يُستورد بأرقام صحيحة أصبحت ثوابت: حرّر خلية إدخال، وأعد الحساب، ولن يتحرك شيء. أما في الإصدارات الحالية فتعود الصيغ حية، فيبقى الخطر المتبقي بناءً لا يُنمذجه المترجم، وقد يترك صيغة تعيد الحساب بشكل مختلف عن التطبيق المُنتِج بينما تُظهر القيمة المخبأة نتيجة المرسل. قارن نتائج إعادة الحساب بالقيم المخبأة المستوردة للصيغ التي يعتمد عليها خط أنابيبك، وأعد إنشاء أي صيغة تختلف من قواعد عملك الخاصة عبر Cell.Formula، التي تأخذ في واجهة XLSX التعبير دون علامة يساوي بادئة
التصميم حول فجوات الرحلة المتبقية
التصدير يُرسم من نموذج المصنف الكامل في الذاكرة: القيم والصيغ والأنماط، وإن طلبتها، المخططات والصور. وتحسّن تصدير الصيغ إصدارًا بعد إصدار. فمنذ 2.384.55 تحافظ المصفوفات المضمّنة واتحادات المراجع والمراجع المطلقة على معناها في OpenFormula؛ ومنذ 2.384.56 يصرّح HotXLS بنطاقات أسماء بادئات صيغه، فيقيّم LibreOffice الصيغ بدل أن يُظهر #VALUE! في كل خلية صيغة؛ ومنذ 2.384.65 تُكتب مراجع العمود الكامل والصف الكامل بصيغة OpenFormula [.A:.A]، فلا يفتحها Excel بعد اليوم كـ#NAME?. وتبعتها التنسيقات الشرطية: فمنذ 2.384.66 تُكتب القواعد المبنية على صيغ كـformula-is(...) مع خليتها الأساسية، وهو ما يعرفه LibreOffice، ومنذ 2.384.69 تُكتب قواعد القيم والصيغ أيضًا كشروط style:map قياسية، وهي الصيغة الوحيدة التي يقرؤها Excel، بينما يقرأ طرف الاستيراد الشكلين معًا. لذا فإن دورة كاملة من .xlsx إلى .ods إلى .xlsx على إصدار حالي تحافظ على القيم والصيغ والأنماط والتنسيقات الشرطية؛ وما زال بإمكانها فقدانه يقع في الثغرات المذكورة في القسم السابق
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // الصيغة تُكتشف تلقائيًا
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// منذ HotXLS 2.88.229 تعود الصيغ حية جنبًا إلى جنب مع نتائجها
// المخبأة؛ أعد إنشاء أي صيغة لا يستطيع خط الأنابيب اللاحق
// الوثوق بترجمتها قبل الحفظ.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
النمط المعماري الذي ينتج عن هذا: عامل ملفات .ods الواردة كتغذيات بيانات، لا كمستندات تُحرَّر في مكانها. أبقِ المصنف المرجعي بصيغة .xlsx، واستخرج القيم من مراجعات العملاء، وأصدر ODS جديدًا عند الطلب من النسخة المرجعية. التحقق ينتمي إلى المعسكرين كليهما - افتح الملفات المصدَّرة في LibreOffice Calc، المستهلك المرجعي لـODF، وفي Excel، الذي يقرأ ODS منذ سنوات لكنه يختلف مع LibreOffice عند أطراف دعم المخططات والأنماط. عدد الأوراق، وحفنة من الخلايا الأساسية، ووجود المخططات تشكّل فحص تدخين كافيًا لكل ملف تصدير
فرز ملف ODS قبل الالتزام باستيراده
عندما تقبل نقطة نهاية رفع الملفات، فإن سرد أسماء الأوراق أرخص بكثير من تحليل كامل ويكشف المفاجآت البنيوية مبكرًا:
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetODSSheetNames('incoming.ods', Names) <= 0 then
raise Exception.Create('not a readable ODS package');
if Names.IndexOf('Data') < 0 then
raise Exception.Create('revision is missing the Data sheet');
finally
Book.Free;
Names.Free;
end;
اصطلاح القيمة المُعادة يوقع الناس: استدعاءات HotXLS تعيد عمومًا عددًا موجبًا أو 1 عند النجاح و-1 عند الفشل، وتفرغ القائمة عند الفشل، لذا اختبر <= 0 بدلًا من المقارنة بقيمة موجبة محددة واحدة. GetODSSheetNames لا تعيد ضبط كائن المصنف ولا تملؤه، لذا يمكن لكائن فحص واحد أن يفحص مجلدًا كاملًا من الملفات الواردة. فحوصات بنيوية كهذه تلتقط أكثر إخفاقات العالم الحقيقي شيوعًا - محلل يعيد تسمية ورقة أو يحذفها قبل إرسال المراجعة - عند البوابة، حيث لا تزال رسالة الخطأ قادرة على تسمية الملف والورقة المفقودة بدلًا من الظهور كمرجع nil على بُعد ثلاث طبقات أعمق
إذا كنت تبني خط أنابيب تحويل أوسع حول هذا، فإن نمط منصة تدقيق المصنفات وتحويلها يوضح كيفية جرد ميزات ملف قبل اختيار صيغة هدف، ودليل أداء المصنفات الكبيرة يبقي عمليات التصدير الدفعية ضمن حدود ذاكرة معقولة
HotXLS مكتبة جداول بيانات أصلية لـDelphi وC++Builder بشيفرة مصدرية كاملة؛ قائمة الميزات الكاملة وتفاصيل الترخيص موجودة على صفحة منتج HotXLS Delphi Component