مقال تقني

HotXLS Delphi Component: ODS open, save, and round-trip في Delphi

توضح المقالة أن دعم OpenDocument موجود على واجهة XLSX لا واجهة XLS، وأن التصدير يستخدم TODSExportOptions. كما تسجل ما يقرأه الاستيراد وما يتجاوزه عمداً، وتتعامل مع round trip غير المتناظر عبر فحص ملف ODS قبل الالتزام بالاستيراد

خلفية تقارير Delphi أصدرت ملفات ‎.xlsx‎ لسنوات تواجه متطلبًا جديدًا: قواعد المشتريات لدى عميل من القطاع العام تفرض خرج OpenDocument Spreadsheet، والمحللون في ذلك الحساب يعيدون تعديلاتهم كملفات ‎.ods‎ محفوظة من LibreOffice. فالآن يجب على الكود نفسه أن يكتب ODS وأن يقرأه أيضًا. HotXLS، مكتبة جداول بيانات Object Pascal الأصلية من losLab لـDelphi وC++Builder، تتولى الاتجاهين دون أي تثبيت لـExcel أو LibreOffice في أي مكان. ما لا تفعله هو جعل الاتجاهين متماثلين. التصدير يحمل أكثر بكثير مما يستعيده الاستيراد، والفريق الذي يفترض غير ذلك سيرى الصيغ والتنسيق تتبخر في مكان ما بين مراجعة العميل والتقرير التالي، دون أي خطأ يشير إليه

دعم 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

مخطط لتخطيط أصناف HotXLS داخل Delphi حيث تسكن كل نقاط دخول ODS على TXLSXWorkbook ويحمل جسر SaveXLSWorkbookAsXLSX محتوى .xls بـ BIFF8 عابرًا
تتعلى كل نقطة دخول OpenDocument على TXLSXWorkbook، ولا يبلغ ملف .xls القديم ODS إلا عبر جسر BIFF إلى XLSX الفاقد

الكشف في جهة الاستيراد تلقائي. طريقة 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 مسار خفيف الوزن عمدًا. إنه يحافظ على قيم الخلايا العددية والنتيجة المخبأة التي حملتها كل صيغة وقت الحفظ، ويوسّع الصفوف والأعمدة المتكررة إلى الشبكة. لكنه لا ينقل الأنماط، ولا تعبيرات صيغ ODS، ولا الرسوم

خيار الصيغ هو الأرجح أن يعضّك، وقد اتُّخذ عمدًا. خلية ODF تخزن شيئين جنبًا إلى جنب: تعبير الصيغة، مكتوبًا بلهجة OpenFormula المعرَّفة في ODF 1.3 الجزء 4، وآخر قيمة حسبها التطبيق المُنتِج لها. ترجمة OpenFormula إلى صياغة صيغ Excel مشكلة تحويل لهجة قائمة بذاتها، بحالات حدّية حقيقية حول مفردات الدوال وصياغة المراجع ونماذج الأخطاء. قراءة القيمة المخبأة بدلًا من ذلك تتجاوز تلك الفئة الكاملة من سوء الترجمة الصامت، لذا فإن الأرقام التي تستوردها هي بالضبط الأرقام التي رآها المرسل آخر مرة. الثمن أنها تصل كأرقام، لا كصيغ حية أنتجتها

نمط الفشل الذي يجب التصميم حوله يتبع ذلك مباشرة: جدول بيانات كانت مجاميعه صحيحة عندما حفظه LibreOffice آخر مرة يُستورد بأرقام صحيحة، لكن تلك الأرقام أصبحت الآن ثوابت. حرّر خلية إدخال، وأعد الحساب، ولن يتحرك شيء - الصيغة اختفت، ولم يبقَ سوى نتيجتها النهائية. إذا احتاج سير العمل صيغًا حية بعد الاستيراد، أعد إنشاءها برمجيًا من قواعد عملك الخاصة عبر Cell.Formula، التي تأخذ في واجهة XLSX التعبير دون علامة يساوي بادئة

التصميم حول الرحلة غير المتماثلة

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

مخطط لرحلة ODS غير المتماثلة في HotXLS من Delphi: تصدير كامل الدقة من نموذج المصنف في الذاكرة واستيراد قيم فقط يترك الصيغ ثوابت
يعرض التصدير النموذج الكامل في الذاكرة بينما يعيد الاستيراد القيم والنتائج المخزنة، فتُسقط دورة .xlsx إلى .ods إلى .xlsx كاملةً الأنماط والصيغ الحية بصمت
Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // الصيغة تُكتشف تلقائيًا
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // القيم ونتائج الصيغ المخبأة موجودة بعد استيراد ODS؛
    // الأنماط والصيغ الحية ليست موجودة. أعد بناء كل ما
    // يعتمد عليه خط الأنابيب اللاحق قبل الحفظ.
    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 قبل الالتزام باستيراده

عندما تقبل نقطة نهاية رفع الملفات، فإن سرد أسماء الأوراق أرخص بكثير من تحليل كامل ويكشف المفاجآت البنيوية مبكرًا:

مخطط لببوابة فرز الرفع في HotXLS داخل Delphi حيث يرفض GetODSSheetNames حزم ODS غير القابلة للقراءة والأوراق الناقصة قبل تشغيل استيراد كامل
فحص GetODSSheetNames يكلف أقل بكثير من تحليل كامل ويلتقط إخفاق الورقة المعاد تسميتها ما دام الخطأ قادراً على تسمية الملف
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