برای تولید یک فایل ODS که هم Excel و هم LibreOffice درست بخوانند، HotXLS هر فرمول را با سینتکس OpenFormula زیر یک namespace اعلانشده یعنی of: مینویسد، و هر فرمت شرطی از جنس مقدار یا فرمول را دوبار مینویسد: بهشکل یک <style:map> روی استایل هر سلول پوششدادهشده که تنها فرمی است که Excel 16 میخواند، و بهشکل یک بلاک calcext:conditional-formats که فرمی است که LibreOffice به آن اعتماد میکند. هر برنامه نیمه مالِ دیگری را نادیده میگیرد، پس فایلی که در یکی درست دیده میشود درباره دیگری هیچ چیزی ثابت نمیکند
آن جمله آخر درس شش نسخه HotXLS بین v2.384.55 و v2.384.72 است. هر فیکس با فایلی شروع شد که HotXLS نوشته بود، خودش بینقص میخواندش، و یکی از دو برنامه هدف غلط میخواندش. آنچه میآید این است که هر برنامه واقعاً چه چیزی میپذیرد، چه نشانهگذاریای هر دو را راضی میکند، و کدام فراخوانیهای API مربوط به HotXLS از دلفی همان را تولید میکنند
چرا یک فایل ODS در یک برنامه سالم و در دیگری خراب دیده میشود؟
یک فایل ODS در یک برنامه سالم و در دیگری خراب دیده میشود چون Excel و LibreOffice بخشهای متفاوتی از همان پکیج را میخوانند. OpenDocument به فرمولها و فرمتهای شرطی بیش از یک املای قانونی میدهد، LibreOffice یک namespace افزونه مال خودش هم رویش میگذارد، و هر مصرفکننده زیرمجموعهای را برمیدارد که پیاده کرده. نویسندهای که فقط مقابل یک مصرفکننده تست شده با کمال میل روی نشانهگذاری همگرا میشود که دیگری بیسروصدا غلط میخواند
هیچکدام از دو برنامه خطا گزارش نمیکنند. LibreOffice در سلولهایی که فرمولهایش را نتوانست parse کند #VALUE! نشان میدهد؛ Excel workbook را با فرمتهای شرطی غایب باز میکند، یا با فرمولی که بازنویسی شده به چیزی که به #NAME? یا ثابت 0 ارزیابی میشود. نویسندهای که خروجی خودش را round-trip میکند هیچکدام را نمیبیند. HotXLS دقیقاً با همان تله سراغ فرمولنویسی namespace رفت: reader آن پیشوند of: را بهعنوان متن ساده مچ میکرد، پس هر round-trip با خودش پاس میشد در حالی که LibreOffice در همه سلولهای فرمولدار #VALUE! نشان میداد
| فیچر | آنچه Excel 16 میخواند | آنچه LibreOffice 26.2 میخواند |
|---|---|---|
ستون کامل نوشتهشده بهشکل A:A | بدفهمیده میشود به A:(A) | تحمل میشود |
ستون کامل نوشتهشده بهشکل [.A:.A] | بله | بله |
فرمتهای شرطی در <style:map> | بله، تنها فرمی که میخواند | وقتی calcext هست نادیده گرفته میشود |
فرمتهای شرطی در calcext:conditional-formats | نادیده گرفته میشود | بله، و ترجیح داده میشود |
قاعده مقداری calcext با ویژگی calcext:operator | نادیده گرفته میشود | بهعنوان «مساوی 0» وارد میشود |
قاعده فرمولی calcext نوشتهشده بهشکل is-true-formula(...) | نادیده گرفته میشود | بهعنوان یک مقایسه مقداری با 0 وارد میشود |
OpenFormula در ODS: اول namespace را اعلان کنید، بعد سینتکس را درست بزنید
یک سلول فرمولدار در ODS فقط وقتی برای LibreOffice خواناست که پیشوند of: در table:formula به یک namespace اعلانشده XML حل شود. پیشوند تزئین نیست. of: مپ میشود به urn:oasis:names:tc:opendocument:xmlns:of:1.2، و msoxl: که پیشوندی است HotXLS برای فرمولهایی که مترجم OpenFormulaاش مدلشان نمیکند استفاده میکند، مپ میشود به http://schemas.microsoft.com/office/excel/formula. قبل از v2.384.56 ریشه content.xml هر دو پیشوند را بدون اعلان استفاده میکرد، و LibreOffice اصلاً نمیتوانست گرامر فرمول را شناسایی کند
<!-- قبل از v2.384.56: پیشوند استفاده شده اما هرگز اعلان نشده؛ LibreOffice مقدار #VALUE! نشان میدهد -->
<office:document-content xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" ...>
<table:table-cell table:formula="of:=SUM([.A1:.A3])" office:value-type="float" office:value="245"/>
<!-- از v2.384.56: هر دو namespace مربوط به فرمول روی ریشه اعلان شدهاند -->
<office:document-content
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2"
xmlns:msoxl="http://schemas.microsoft.com/office/excel/formula" ...>
با فیکس شدن namespace، خود عبارت هم همچنان باید یک OpenFormula معتبر باشد، طبق تعریف بخش 4 از OpenDocument 1.3. تلهها جاهاییاند که سینتکس اکسل و OpenFormula شبیه به نظر میرسند اما یکی نیستند:
- ارجاعهای سلولی براکتدار و با نقطه پیشوندند، و علامتهای
$جزو ارجاعاند: [.$A$1]و[.A$1:.$B2]یک OpenFormula معتبرند. قبل از v2.384.55 نویسنده HotXLS همه$ها را میانداخت، پس ارجاعهای مطلق نسبی برمیگشتند و فقط وقتی خراب میشدند که کسی سلول را کپی میکرد - ستونها و سطرهای کامل باید فرم براکتدار را بگیرند:
[.A:.A]و[.$A:.$B]و[.1:.1]و[.$1:.$2]. یکof:=SUM(A:A)لخت را LibreOffice تحمل میکند، اما Excel 16 بازش میکند بهشکل=SUM(A:(A))با#NAME?، و ارجاعهای سطری و$A:$Bرا به ثابت 0 میبرد. HotXLS فرم براکتدار را از v2.384.65 مینویسد - آرگومانهای تابع با
;جدا میشوند، نه, - اجتماع ارجاعها از عملگر
~استفاده میکند: مقدارAREAS((A1,B2))اکسل میشودAREAS(([.A1]~[.B2])). ترجمه همان کاما به;بهجای آن، یک آرگومان اجتماع را به دو آرگومان میبرد - آرایههای درونخطی ستونها را با
;و سطرها را با|جدا میکنند: مقدار{1,2;3,4}اکسل میشود{1;2|3;4}. قبل از v2.384.55 HotXLS مقدار{1;2;3;4}تولید میکرد، یک سطر از چهار مقدار
کاما بخش سخت است، چون یک کاراکتر اکسل سه معنا حمل میکند. از v2.384.55 نویسنده HotXLS موقع ترجمه یک stack پرانتز دنبال میکند: یک ( بلافاصله بعد از یک نام یک فراخوانی تابع باز میکند که کاماهایش ; میشوند؛ هر ( دیگری یک پرانتز گروهبندی است که کاماهایش ~ میشوند؛ و کاماهای داخل {} جداکننده ستون آرایهاند. با این و فیکس namespace، LibreOffice 26.2 هر هشت فرمول probe آرایه و اجتماع را درست ارزیابی کرد، از جمله INDEX و AREAS روی اجتماعها
uses
lxHandleX;
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Orders');
Sheet.Cells[1, 1].Value := 120;
Sheet.Cells[2, 1].Value := 80;
Sheet.Cells[3, 1].Value := 45;
Sheet.Cells[1, 2].Value := 0.2;
// از v2.384.65 نوشته میشود بهشکل of:=SUM([.A:.A])
Sheet.Cells[1, 4].Formula := 'SUM(A:A)';
// نوشته میشود بهشکل of:=[.A1]*[.$B$1]؛ علامتهای $ از v2.384.55 سالم میمانند
Sheet.Cells[2, 4].Formula := 'A1*$B$1';
Book.SaveAsODS('orders.ods');
finally
Book.Free;
end;
end;
فرمولهایی که مترجم مدلشان نمیکند به msoxl:= با همان متن اکسل برمیگردند، و برای همین اعلان msoxl هم مهم است. در نویسنده فعلی این مسیر شامل ارجاعهای واجد-نام-شیت مثل Sheet2!A1 و ارجاعهای ساختاریافته جدول است. HotXLS فرمولهای msoxl: را موقع import برمیگرداند و میخواند، پس round-trip خودش عبارت را سالم نگه میدارد، اما برنامه دیگری با آنها چه کند از کنترل نویسنده خارج است. اگر فرمولی که مصرفکنندههایتان به آن تکیه دارند با پیشوند msoxl: دربیاید، قبل از ارسال فایل را در هر دو برنامه باز کنید
چرا Excel فرمتهای شرطیای که فقط calcext نوشته شدهاند را نمیبیند؟
Excel 16 فرمتهای شرطی calcext را نمیبیند چون فرمتهای شرطی ODS را انحصاراً از فرزندان <style:map> استایلهای سلولی میخواند و بلاک calcext:conditional-formats را کلاً نادیده میگیرد. آزمایشی که این را حل میکند کوتاه است: یک ODS ذخیرهشده توسط LibreOffice بردارید، المانهای style:map را حذف کنید، و Excel صفر قاعده میخواند؛ بهجایش بلاک calcext را حذف کنید، و Excel همچنان همه را میخواند. LibreOffice برعکس رفتار میکند. calcext همان namespace افزونه LibreOffice است و بخش استاندارد ODF نیست، و وقتی یک قاعده calcext حاضر باشد LibreOffice همان را برمیدارد و style:map را نادیده میگیرد
قبل از v2.384.69 HotXLS فقط calcext مینوشت، پس یک فایل ODS با هایلایتهای کاملاً سالم در Excel بدون هیچ قاعده مقداری و هیچ قاعده فرمولی باز میشد. HotXLS حالا هر دو فرم را مینویسد. نیمه style:map از گرامر شرط اسکیمای OpenDocument استفاده میکند (بخش 3 از ODF 1.3)، با همین املاهایی که Excel 16 و LibreOffice 26.2 هر دو موقع ذخیره ODS تولید میکنند:
<!-- سادهشده. استایل حامل برای هر سلول A1:A50 (دو قاعده مقداری) -->
<style:style style:name="ce3" style:family="table-cell">
<style:map style:condition="cell-content()>100"
style:apply-style-name="CF_Hit"
style:base-cell-address="Orders.A1"/>
<style:map style:condition="cell-content-is-between(1,10)"
style:apply-style-name="CF_Low"
style:base-cell-address="Orders.A1"/>
</style:style>
<!-- استایل حامل برای هر سلول C1:C50 (یک قاعده فرمولی) -->
<style:style style:name="ce4" style:family="table-cell">
<style:map style:condition="is-true-formula(COUNTIF([.$C:.$C];[.C1])>1)"
style:apply-style-name="CF_Dup"
style:base-cell-address="Orders.C1"/>
</style:style>
گیر style:map این است که روی استایلهای سلولی زندگی میکند، پس بهازای هر سلول است. هر سلول در بازه قاعده باید استایلی داشته باشد که مپ را حمل کند، سلولهای خالی هم شامل، وگرنه قاعده در Excel آن سلول را اصلاً پوشش نمیدهد. HotXLS استایل فرمت موجود هر سلول را کپی میکند، مپها را اضافه میکند، و استایلهای حامل را بر اساس جفت استایل-اصلی و متن-مپ deduplicate میکند، پس بازهای از 500 سلول با فرمت یکسان همچنان یک استایل تولید میکند. نویسنده جدول نوشتهشده را تا بازه قاعده هم گسترش میدهد، یعنی سطرهای تهی انتهایی داخل یک قاعده صادر میشوند نه اینکه افتاده شوند. از v2.384.69 مقدار styles.xml هم یک استایل سلولی خالی به نام Default حمل میکند تا style:apply-style-name="Default" همیشه هدف داشته باشد
امالای calcext ای که LibreOffice واقعاً میپذیرد
LibreOffice یک قاعده مقداری calcext را فقط وقتی میپذیرد که عملگر مقایسه بخشی از متن مقدار باشد، مثل >3 یا between(1,10)، و قاعده فرمولی را فقط وقتی که بهشکل formula-is(...) نوشته شده باشد. هر دو نکته یک نسخه از HotXLS گرفتند، چون امالای غلط قاعدهای تولید میکنند که بدون خطا وارد میشود و بعد سلولهای اشتباه را مچ میکند
اشتباه اول یک ویژگی calcext:operator کنار calcext:value بود. طبیعی خوانده میشود، اما از خودساخته است: LibreOffice آن ویژگی را نمیشناسد، پس هر قاعده مقداری را بهعنوان «مساوی 0» وارد میکرد. اشتباه دوم گذاشتن is-true-formula(...)، همان املای style:map، داخل یک شرط calcext بود که LibreOffice آن را هم بهعنوان مقایسه مقدار-سلولی با 0 وارد میکرد. فیکس فرمولی در v2.384.66 و فیکس مقداری در v2.384.69 منتشر شد:
<!-- غلط: LibreOffice مقدار calcext:operator را نادیده میگیرد و «مساوی 0» وارد میکند -->
<calcext:condition calcext:apply-style-name="CF_Hit"
calcext:operator="greater-than" calcext:value="100"/>
<!-- درست: عملگر داخل خود مقدار سفر میکند -->
<calcext:condition calcext:apply-style-name="CF_Hit"
calcext:value=">100" calcext:base-cell-address=".A1"/>
<calcext:condition calcext:apply-style-name="CF_Low"
calcext:value="between(1,10)" calcext:base-cell-address=".A1"/>
<!-- درست: قاعدههای فرمولی از formula-is استفاده میکنند، ارجاع نسبی لنگرشده به سلول پایه -->
<calcext:condition calcext:apply-style-name="CF_Dup"
calcext:value="formula-is(COUNTIF([.$C:.$C];[.C1])>1)"
calcext:base-cell-address=".C1"/>
سلول پایه همان چیزی است که به ارجاعهای نسبی معنا میدهد. HotXLS هر قاعده را در سلول بالا-چپ اولین ناحیه بازهاش لنگر میکند، پس فرمولی که برای C1 نوشته شده بهشکل C2 و C3 و همینطور به پایین بازه ارزیابی میشود، دقیقاً مثل فرمتبندی شرطی خود اکسل. عبارت قاعده از همان مترجمی میگذرد که فرمولهای سلولی، پس آرایهها و اجتماعها و ستونهای کامل و علامتهای $ به همان فرمهایی درمیآیند که بالا توضیح داده شد. در سمت Delphi قاعدهها را دقیقاً مثل یک فایل .xlsx اضافه میکنید
uses
lxHandleX;
procedure AddOrderHighlights(Book: TXLSXWorkbook; Sheet: TXLSXWorksheet);
var
Idx: Integer;
Opts: TODSExportOptions;
begin
// قاعدههای مقداری: مقدار style:map یعنی cell-content()>100 بهعلاوه مقدار calcext یعنی ">100"
Idx := Sheet.AddConditionalFormat('A1:A50', xlsxCfOpGreaterThan, '100');
Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($00C0C0FF); // BGR: قرمز روشن
Idx := Sheet.AddConditionalFormat('A1:A50', xlsxCfOpBetween, '1', '10');
Sheet.ConditionalFormats[Idx].Style.SetFontBold(True);
// قاعده فرمولی با سینتکس اکسل (جداکنندهها کاما، نسبت به C1):
// مقدار style:map یعنی is-true-formula(...) بهعلاوه مقدار calcext یعنی formula-is(...)
Idx := Sheet.AddCondFormatExpression('C1:C50', 'COUNTIF($C:$C,C1)>1');
Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($00CCFFFF); // BGR: زرد روشن
Opts := TODSExportOptions.Create;
try
Opts.Generator := 'OrderExport 3.1';
Book.SaveAsODS('orders.ods', Opts);
finally
Opts.Free;
end;
end;
خواندن ODS تولیدشده توسط Excel و LibreOffice به دلفی
وقتی HotXLS یک فایل ODS باز میکند، reader آن هر دو لهجه فرمت شرطی و هر دو امالای calcext را میپذیرد، و وقتی فایل قاعدهای را در هر دو فرم حمل کند آن را دوبار نمیشمارد. فایلهای واقعی از سه نویسنده میآیند، هر کدام با عادت خودش:
- calcext قدیمی و جدید. فایلهای دارای ویژگی
calcext:operator، از جمله ODS نوشتهشده توسط HotXLS قبل از v2.384.69، همچنان از parse قدیمی میگذرند. شرطهای فرمولی هم بهعنوانformula-is(...)شناخته میشوند هم بهعنوانis-true-formula(...) - امالای style:map مربوط به Excel. Excel شرطها را با پیشوند
of:مینویسد، مثلof:cell-content-is-between(1,10)، و روی قاعدههای مقداری سلول پایه را حذف میکند. هر دو پذیرفته میشوند - سلولهای خالی. Excel و LibreOffice هر دو مپ مربوط به سلولهای خالی را روی استایل پیشفرض ستون میگذارند نه روی سلول، پس reader استایلهای پیشفرض ستون را برای سلولهای تکراری قبل از جمع کردن مپها حل میکند
- بازسازی ناحیه. مپها بهازای هر سلول جمع میشوند، پس بعد از خواندن یک شیت، reader سلولهایی که شرط و سلول پایه یکسانی دارند را به بازه برمیگرداند، اول در طول هر سطر و بعد به پایین در spanهای ستونی همخوان، و هر قاعدهای را که از قبل از calcext خوانده حذف میکند
فیکس v2.384.72 مربوط به استایلهای عددی است نه قاعدهها. Excel 16 و LibreOffice 26.2 هر دو فرمت General را بهشکل یک استایل عددی مینویسند که المان number:number اش هیچ number:decimal-places ای ندارد، معمولاً <number:number number:min-integer-digits="1"/>. reader مربوط به HotXLS نبود شمارنده را دو رقم اعشار ثابت میگرفت، پس هر مقدار زیر استایل Default با 0.00 وارد میشد و 1.5 بهشکل 1.50 نمایش داده میشد. از v2.384.72 یک المان عددی ساده بدون رقم اعشار و بدون حداقل اعشار و بدون گروهبندی و با حداکثر یک رقم صحیح مپ میشود به General، و یک General تنها سلول را بدون هیچ فرمت عددی میگذارد. متن دورش نگه داشته میشود، مثل General" kg"، و اعداد گروهبندیشده مپ قبلی را نگه میدارند چون اکسل فرمت General گروهبندیشده ندارد
uses
SysUtils, lxCondFormat, lxHandleX;
procedure DumpOdsRules(const FileName: string);
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Rule: TXLSXConditionalFormat;
I: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open(FileName) <= 0 then
raise Exception.Create('cannot open ' + FileName);
if Book.SourceFormat <> xlsxOpenDocumentSpreadsheet then
raise Exception.Create('not an ODS package');
Sheet := Book.Sheets[1]; // اندیسگذار Sheets یک-مبناست
for I := 0 to Sheet.ConditionalFormats.Count - 1 do
begin
Rule := Sheet.ConditionalFormats[I];
case Rule.Kind of
cfkCellIs:
Writeln(Rule.Range, ' value rule ', Ord(Rule.Op), ' ',
Rule.Formula1, ' ', Rule.Formula2);
cfkExpression:
Writeln(Rule.Range, ' formula rule ', Rule.Formula1);
end;
end;
// سلولی با استایل General مربوط به اکسل از v2.384.72 بدون هیچ
// فرمت عددی برمیگردد، بهجای '0.00'
Writeln('A2 format: "', Sheet.Cells[2, 1].NumberFormat, '"');
finally
Book.Free;
end;
end;
فرمولهای قاعده با سینتکس اکسل و جداکنندههای کاما برمیگردند، همان فرمی که به AddCondFormatExpression میدادید، پس قاعدهای که HotXLS نوشته بهعنوان همان رشته عیناً برمیگردد. برای تصویر کلانتر از اینکه مسیر import مربوط به ODS چه چیزی را نگه میدارد و چه چیزی را میاندازد، راهنمای round-trip باز کردن و ذخیره ODS در HotXLS را ببینید؛ و برای اینکه سطرهای تکراری از Excel و LibreOffice موقع import چطور باز میشوند، سطرهای تکراری ODS بهعنوان رانهای ارتفاع سطر را ببینید
محدودیتهای interop فرمت شرطی ODS در HotXLS چیست؟
رویکرد نشانهگذاری-دوگانه قاعدههای مقایسه مقداری و قاعدههای فرمولی را پوشش میدهد و همانجا میایستد. بقیه چیزها یکطرفهاند یا اصلاً نوشته نمیشوند:
- مقیاسهای رنگی و data barها فقط بهعنوان المان calcext نوشته میشوند، پس LibreOffice نشانشان میدهد و Excel نه
- انواع دیگر قاعده مثل مجموعه آیکون و قاعدههای متنی و top-N و بالاتر-از-میانگین و قاعدههای تکراری، در نویسنده فعلی خروجی ODS ندارند. یک قاعده متنی معمولاً میشود بهشکل قاعده فرمولی بازنویسیش کرد، مثل
ISNUMBER(SEARCH("late",B2))رویB2:B200که بعد به هر دو برنامه میرسد - قاعدههای کل-ستون و کل-سطر مثل
C:Cفقط روی ناحیه جدولی گذاشته میشوند که واقعاً نوشته شده، نه روی هر 1,048,576 سطر، پس Excel این قاعدهها را فقط روی سلولهایی میبیند که در فایل وجود دارند - فایلهای فقط-style:map. وقتی فایل بلاک calcext ندارد، HotXLS ارجاعهای نسبی در قاعدههای فرمولی را از گوشه بالا-چپ بازه بازسازیشده تفسیر میکند، نه با شیفت از سلول پایه اعلامشده
- قاعدههای همپوشان از LibreOffice. وقتی یک سلول زیر چند قاعده است، LibreOffice فقط مپ قاعده اول را رویش مینویسد. چنین فایلهایی را نمیشود کامل فقط از
style:mapخواند، که یک دلیل دیگر برای ترجیح reader به calcext وقتی هر دو هستند
محدودیت فرایندی از همه اینها مهمتر است. نقصهای پشت این نسخهها از round-tripهایی سر درمیآوردند که ODS مینوشتند و با HotXLS برمیگرداندند، و بعضیها یک چک دستی در برنامه غلط را هم رد میکردند: فرمولهای کل-ستون در LibreOffice کار میکردند در حالی که Excel نشان میداد #NAME?، و از v2.384.66 قاعدههای فرمولی در LibreOffice کار میکردند در حالی که Excel تا v2.384.69 هنوز هیچ قاعدهای نشان نمیداد. اگر interop با ODS یک الزام است، تست پذیرش باز کردن فایل در Excel و در LibreOffice و مقایسه چیزی است که هر یک نشان میدهد. همان نظم برای استایلهایی که قاعدهها به آنها اشاره میکنند هم صدق میکند؛ مقاله فرمتبندی شرطی و استایلها در HotXLS پوشش میدهد استایلهای هایلایت در سمت workbook چطور تعریف میشوند
مرجع سریع: ODS ای که هر دو برنامه میخوانند
- روی ریشه
content.xmlمقدارهایxmlns:ofوxmlns:msoxlرا اعلان کنید، وگرنه LibreOffice برای هر فرمولی#VALUE!نشان میدهد (HotXLS از v2.384.56) - ارجاعها را بهشکل
[.A1]بنویسید، همه$ها را نگه دارید، و ستونها و سطرهای کامل را بهشکل[.A:.A]و[.1:.1]بنویسید (از v2.384.55 و v2.384.65) - برای آرگومانها از
;، برای اجتماع ارجاعها از~، و بین سطرهای آرایه درونخطی از|استفاده کنید - هر قاعده مقداری یا فرمولی را برای Excel بهشکل یک
<style:map>روی استایل هر سلول پوششدادهشده بنویسید، و برای LibreOffice بهشکل یک شرط calcext (از v2.384.69) - در calcext عملگر را داخل مقدار بگذارید (
>3وbetween(1,10)) و قاعدههای فرمولی را بهشکلformula-is(...)با یک سلول پایه بنویسید (از v2.384.66 و v2.384.69) - موقع import انتظار یک استایل عددی General بدون
number:decimal-placesرا داشته باشید؛ HotXLS آن را از v2.384.72 بهعنوان General میخواند - هر پروفایل صادرات جدید را با باز کردن فایل در هر دوی Excel و LibreOffice تأیید کنید، هرگز فقط در یکی
HotXLS یک کتابخانه صفحهگسترده بومی Delphi و C++Builder است که XLS و XLSX و ODS را بدون نصب بودن Excel یا LibreOffice میخواند و مینویسد؛ سورس کامل، لیست فیچرها و لایسنس در صفحه کامپوننت صفحهگسترده HotXLS در Delphi است