เพื่อผลิตไฟล์ ODS ที่ทั้ง Excel และ LibreOffice อ่านถูกต้อง HotXLS เขียนสูตรทุกตัวเป็นไวยากรณ์ OpenFormula ใต้ namespace of: ที่ประกาศไว้ และเขียน conditional format แบบค่าหรือสูตรซ้ำสองรอบ เป็นทั้ง <style:map> บนสไตล์ของเซลล์ที่กฎครอบทุกเซลล์ ซึ่งเป็นรูปเดียวที่ Excel 16 อ่าน และบล็อก calcext:conditional-formats ซึ่งเป็นรูปที่ LibreOffice เชื่อ แต่ละแอปพลิเคชันเมินครึ่งที่เขียนไว้ให้อีกตัว ไฟล์ที่โชว์ถูกต้องในแอปใดแอปหนึ่งจึงไม่พิสูจน์อะไรกับอีกแอปเลย
ประโยคสุดท้ายนั่นแหละคือบทเรียนหลังรีลีส HotXLS หกรอบระหว่าง v2.384.55 ถึง v2.384.72 การแก้แต่ละรอบเริ่มจากไฟล์ที่ HotXLS เขียนเอง อ่านกลับเองได้เพอร์เฟกต์ แล้วหนึ่งในสองแอปเป้าหมายกลับอ่านเพี้ยน สิ่งที่ตามมาคือแต่ละแอปรับจริง ๆ ว่าอะไร markup แบบไหนที่อยู่ได้ทั้งสองฝั่ง และ call ของ HotXLS API ที่ผลิตมันได้จาก Delphi
ทำไมไฟล์ ODS ถึงดูปกติในแอปหนึ่งแต่พังในอีกแอป
ไฟล์ ODS ดูปกติในแอปหนึ่งแต่พังในอีกแอป เพราะ Excel กับ LibreOffice อ่านคนละส่วนของแพ็กเกจเดียวกัน OpenDocument ให้สูตรกับ conditional format มีการสะกดที่ถูกต้องได้มากกว่าหนึ่งแบบ LibreOffice เติม namespace extension ของตัวเองขึ้นมาอีกชั้น แล้วแต่ละ consumer ก็หยิบเฉพาะส่วนย่อยที่ตัวเอง implement เขียนโดยเทสต์กับ consumer เดียวจึงมักลู่ไปเขียน markup ที่อีกฝั่งอ่านเพี้ยนเงียบ ๆ ได้สบาย
ไม่มีแอปไหนรายงาน error LibreOffice โชว์ #VALUE! ในเซลล์ที่ parse สูตรไม่ได้ Excel เปิด workbook โดย conditional format หายวับไปเลย หรือสูตรถูกแปลงเป็นอะไรที่ประเมินได้ #NAME? หรือค่าคงที่ 0 writer ที่ round-trip กับ output ตัวเองจะไม่เคยเห็นอะไรพวกนี้เลย HotXLS เคยตกกับดักพอดีตัวกับเรื่อง namespace ของสูตร reader ของมัน match prefix of: เป็นข้อความธรรมดา ทุก round trip กับตัวเองจึงผ่านหมด ทั้งที่ LibreOffice โชว์ #VALUE! ในเซลล์สูตรทุกเซลล์
| ฟีเจอร์ | Excel 16 อ่านได้ | LibreOffice 26.2 อ่านได้ |
|---|---|---|
คอลัมน์ทั้งคอลัมน์เขียนเป็น A:A | อ่านผิดเป็น A:(A) | ยอมได้ |
คอลัมน์ทั้งคอลัมน์เขียนเป็น [.A:.A] | ได้ | ได้ |
conditional format ในรูป <style:map> | ได้ เป็นรูปเดียวที่มันอ่าน | เมินเมื่อมี calcext |
conditional format ในรูป calcext:conditional-formats | เมิน | ได้ และถือเป็นตัวเลือกหลัก |
กฎค่า calcext ที่มี attribute calcext:operator | เมิน | import เป็น "เท่ากับ 0" |
กฎสูตร calcext ที่สะกด is-true-formula(...) | เมิน | import เป็นการเทียบค่ากับ 0 |
OpenFormula ใน ODS: ประกาศ namespace ก่อน แล้วค่อยลงลึกไวยากรณ์
เซลล์สูตรใน ODS จะอ่านได้ด้วย LibreOffice ก็ต่อเมื่อ prefix of: ใน table:formula ชี้ไปยัง XML namespace ที่ประกาศไว้จริง prefix ไม่ใช่ของประดับ of: ชี้ไปที่ urn:oasis:names:tc:opendocument:xmlns:of:1.2 และ msoxl: ซึ่งเป็น prefix ที่ HotXLS ใช้กับสูตรที่ตัวแปล OpenFormula ยังไม่รองรับ ชี้ไปที่ http://schemas.microsoft.com/office/excel/formula ก่อน v2.384.56 root ของ content.xml ใช้ทั้งสอง prefix โดยไม่ประกาศ LibreOffice จึงระบุไวยากรณ์ของสูตรไม่ได้เลย
<!-- ก่อน v2.384.56: ใช้ prefix แต่ไม่เคยประกาศ 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 ของสูตรทั้งสองไว้ที่ root -->
<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 ที่ถูกต้องตามที่ OpenDocument 1.3 Part 4 นิยาม กับดักอยู่ตรงที่ไวยากรณ์ Excel กับ OpenFormula ดูใกล้เคียงกันแต่ไม่เหมือนกันในหลายจุด:
- reference เซลล์ต้องมีวงเล็บและนำหน้าด้วยจุด โดยเครื่องหมาย
$เป็นส่วนหนึ่งของ reference:[.$A$1]กับ[.A$1:.$B2]เป็น OpenFormula ที่ถูกต้อง ก่อน v2.384.55 writer ของ HotXLS ทิ้ง$ทุกตัว reference แบบ absolute จึงกลับมาเป็น relative และพังเฉพาะตอนที่มีคนก๊อปปี้เซลล์เท่านั้น - คอลัมน์กับแถวทั้งชุดต้องใช้รูปแบบมีวงเล็บ
[.A:.A],[.$A:.$B],[.1:.1],[.$1:.$2]of:=SUM(A:A)เปล่า ๆ LibreOffice ยอมได้ แต่ Excel 16 เปิดเป็น=SUM(A:(A))พร้อม#NAME?และเปลี่ยน reference แถวกับ$A:$Bเป็นค่าคงที่ 0 HotXLS เขียนรูปแบบมีวงเล็บตั้งแต่ v2.384.65 - อาร์กิวเมนต์ฟังก์ชันคั่นด้วย
;ไม่ใช่, - union ของ referenceใช้ operator
~: ExcelAREAS((A1,B2))กลายเป็นAREAS(([.A1]~[.B2]))ถ้าแปลคอมมานั้นเป็น;จะได้อาร์กิวเมนต์ union หนึ่งตัวกลายเป็นสองอาร์กิวเมนต์แทน - array แบบ inlineคั่นคอลัมน์ด้วย
;และคั่นแถวด้วย|: Excel{1,2;3,4}กลายเป็น{1;2|3;4}ก่อน v2.384.55 HotXLS ผลิต{1;2;3;4}ออกมา คือแถวเดียวมีสี่ค่า
คอมมาคือส่วนที่ยากที่สุด เพราะอักขระ Excel ตัวเดียวแบกสามความหมาย ตั้งแต่ v2.384.55 writer ของ HotXLS ตาม stack ของวงเล็บระหว่างแปล: ( ที่ต่อท้ายชื่อโดยตรงเปิดการเรียกฟังก์ชัน คอมมาในนั้นกลายเป็น ; ( อื่น ๆ เป็นวงเล็บจับกลุ่ม คอมมาในนั้นกลายเป็น ~ และคอมมาใน {} เป็นตัวคั่นคอลัมน์ของ array ด้วยสิ่งนี้บวกการแก้ namespace LibreOffice 26.2 ประเมินสูตร probe แบบ array กับ union ทั้งแปดตัวได้ถูกต้อง INDEX กับ AREAS บน union รวมอยู่ด้วย
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;
// เขียนเป็น of:=SUM([.A:.A]) ตั้งแต่ v2.384.65
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:= พร้อมข้อความ Excel เดิมไม่เปลี่ยน นี่คือเหตุที่การประกาศ msoxl สำคัญด้วย ใน writer ปัจจุบันเส้นทางนี้รวม reference ที่ระบุชื่อชีตอย่าง Sheet2!A1 กับ structured table reference HotXLS อ่านสูตร msoxl: กลับมาตอน import ได้ การ round trip กับตัวเองจึงคงนิพจน์เดิมไว้ แต่แอปอื่นจะปฏิบัติกับมันอย่างไรอยู่นอกเหนือการควบคุมของ writer ถ้าสูตรที่ consumer ของคุณพึ่งพาออกมามี prefix msoxl: เปิดไฟล์ในทั้งสองแอปก่อนส่งมอบ
ทำไม Excel ถึงไม่เห็น conditional format ที่เขียนไว้แต่ในรูป calcext
Excel 16 ไม่เห็น conditional format แบบ calcext เพราะมันอ่าน conditional format ของ ODS จากลูก <style:map> ของ cell style เท่านั้น และเมินบล็อก calcext:conditional-formats ทั้งก้อน การทดลองที่ปิดเรื่องนี้สั้นมาก หยิบ ODS ที่ LibreOffice เซฟ ลบ element style:map ทิ้ง Excel จะอ่านได้ศูนย์กฎ ลบบล็อก calcext แทน Excel ยังอ่านได้ครบทุกกฎ LibreOffice ทำตรงข้ามทั้งหมด calcext เป็น namespace extension ของ LibreOffice ไม่ใช่ส่วนหนึ่งของมาตรฐาน ODF และเมื่อมีกฎ calcext อยู่ LibreOffice หยิบมันแล้วเมิน style:map ทิ้ง
ก่อน v2.384.69 HotXLS เขียนแค่ calcext ไฟล์ ODS ที่ไฮไลต์สวยงามเปิดใน Excel จึงไม่เหลือทั้งกฎค่าและกฎสูตรเลย ตอนนี้ HotXLS เขียนทั้งสองรูป ครึ่ง style:map ใช้ไวยากรณ์เงื่อนไขของ schema OpenDocument (ODF 1.3 Part 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 คือมันอาศัยอยู่บน cell style จึงเป็นรายเซลล์ เซลล์ทุกตัวในช่วงของกฎต้องแบกสไตล์ที่ถือ map นั้น เซลล์ว่างก็รวมอยู่ด้วย ไม่งั้นกฎไม่ครอบเซลล์นั้นในสายตา Excel HotXLS ก๊อปปี้สไตล์จัดรูปแบบเดิมของแต่ละเซลล์ เติม map เข้าไป แล้วทำ carrier style ให้ไม่ซ้ำโดยใช้คู่ของสไตล์เดิมกับข้อความ map เป็นกุญแจ ช่วง 500 เซลล์ที่จัดรูปแบบเหมือนกันหมดจึงยังผลิตสไตล์เดียว writer ยังกางตารางที่เขียนออกไปครอบช่วงของกฎ แถวว่างท้าย ๆ ที่อยู่ในกฎจึงถูกปล่อยออกมาแทนที่จะถูกตัดทิ้ง ตั้งแต่ v2.384.69 styles.xml ยังแบก cell style Default ที่ว่างเปล่าไว้ด้วย style:apply-style-name="Default" จึงมีเป้าหมายรออยู่เสมอ
การสะกด calcext ที่ LibreOffice รับจริง
LibreOffice รับกฎค่าแบบ calcext ก็ต่อเมื่อ operator การเปรียบเทียบอยู่ในข้อความค่า เช่น >3 หรือ between(1,10) และรับกฎสูตรก็ต่อเมื่อสะกดว่า formula-is(...) ทั้งสองจุดแพงให้ HotXLS คนละรีลีส เพราะการสะกดผิดผลิตกฎที่ import ผ่านไม่มี error แล้วไปจับคู่เซลล์ผิด ๆ แทน
พลาดแรกคือ attribute calcext:operator ที่วางคู่กับ calcext:value อ่านรู้เรื่องดี แต่มันคือของแต่งขึ้น LibreOffice ไม่รู้จัก attribute นั้น กฎค่าทุกกฎจึงถูก import เป็น "เท่ากับ 0" พลาดที่สองคือเอา is-true-formula(...) ซึ่งเป็นการสะกดแบบ style:map ไปใส่ในเงื่อนไข calcext ซึ่ง LibreOffice ก็ import เป็นการเทียบค่าเซลล์กับ 0 เหมือนกัน การแก้ฝั่งสูตรออกมาใน v2.384.66 และฝั่งค่าใน v2.384.69:
<!-- ผิด: LibreOffice เมิน calcext:operator แล้ว import เป็นเท่ากับ 0 -->
<calcext:condition calcext:apply-style-name="CF_Hit"
calcext:operator="greater-than" calcext:value="100"/>
<!-- ถูก: operator เดินทางอยู่ใน value -->
<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 reference สัมพัทธ์ anchor ที่ base cell -->
<calcext:condition calcext:apply-style-name="CF_Dup"
calcext:value="formula-is(COUNTIF([.$C:.$C];[.C1])>1)"
calcext:base-cell-address=".C1"/>
base cell คือสิ่งที่ให้ reference สัมพัทธ์มีความหมาย HotXLS anchor ทุกกฎที่เซลล์ซ้ายบนสุดของ area แรกในช่วงของมัน สูตรที่เขียนสำหรับ C1 จึงประเมินเป็น C2, C3 ไล่ลงไปตามช่วง ตรงกับที่ conditional formatting ของ Excel เองทำ นิพจน์ของกฎวิ่งผ่านตัวแปลตัวเดียวกับสูตรเซลล์ array, union, คอลัมน์ทั้งชุด กับเครื่องหมาย $ จึงออกมาเป็นรูปแบบที่เล่าไว้ข้างบน ฝั่ง Delphi คุณเพิ่มกฎเหมือนกับที่ทำกับไฟล์ .xlsx เป๊ะ ๆ
uses
lxHandleX;
procedure AddOrderHighlights(Book: TXLSXWorkbook; Sheet: TXLSXWorksheet);
var
Idx: Integer;
Opts: TODSExportOptions;
begin
// กฎค่า: style:map cell-content()>100 บวก calcext value ">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);
// กฎสูตรในไวยากรณ์ Excel (คั่นด้วยคอมมา อ้างอิงสัมพัทธ์กับ 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 กลับเข้า Delphi
เมื่อ HotXLS เปิดไฟล์ ODS reader ของมันรับทั้งสองภาษาถิ่นของ conditional format และทั้งสองการสะกดของ calcext และไม่นับกฎซ้ำสองครั้งเมื่อไฟล์แบกมันไว้ทั้งสองรูป ไฟล์จริงมาจาก writer สามพันธุ์ แต่ละพันธุ์มีนิสัยของตัวเอง:
- calcext เก่ากับใหม่ ไฟล์ที่มี attribute
calcext:operatorรวมถึง ODS ที่ HotXLS เขียนก่อน v2.384.69 ยังวิ่งผ่านการ parse แบบ legacy เงื่อนไขสูตรถูกจับได้ทั้งที่สะกดformula-is(...)และis-true-formula(...) - การสะกด style:map ของ Excel Excel นำหน้าเงื่อนไขด้วย
of:เช่นof:cell-content-is-between(1,10)และละ base cell ในกฎค่า ทั้งสองอย่างถูกรับได้ - เซลล์ว่าง ทั้ง Excel กับ LibreOffice วาง map ของเซลล์ว่างไว้บนสไตล์เริ่มต้นของคอลัมน์ ไม่ใช่บนเซลล์ reader จึง resolve สไตล์เริ่มต้นของคอลัมน์ให้เซลล์ที่ใช้ repeated ก่อนเก็บ map
- การสร้างช่วงกลับ map ถูกเก็บเป็นรายเซลล์ หลังอ่านชีตจบ reader จะรวมเซลล์ที่แชร์เงื่อนไขเดียวกับ base cell เดียวกันกลับเป็นช่วง รวมขวางแถวก่อนแล้วค่อยไล่ลงตามช่วงคอลัมน์ที่ตรงกัน แล้วทิ้งกฎที่อ่านไปแล้วจาก calcext
การแก้ใน v2.384.72 เกี่ยวกับ number style ไม่ใช่กฎ Excel 16 กับ LibreOffice 26.2 เขียนฟอร์แมต General เป็น number style ที่ element number:number ของมันไม่มี number:decimal-places มักหน้าตาเป็น <number:number number:min-integer-digits="1"/> reader ของ HotXLS ถือค่าที่หายไปเป็นทศนิยมคงที่สองตำแหน่ง ทุกค่าในสไตล์ Default จึง import พร้อม 0.00 และ 1.5 แสดงเป็น 1.50 ตั้งแต่ v2.384.72 element ตัวเลขธรรมดาที่ไม่มีทศนิยม ไม่มีจำนวนทศนิยมต่ำสุด ไม่มีการจัดกลุ่ม และมีเลขจำนวนเต็มไม่เกินหนึ่งหลัก จะแมปกับ General และ General เดี่ยว ๆ จะทิ้งเซลล์ไว้โดยไม่มี number format เลย ข้อความรอบตัวมันถูกเก็บไว้ เช่น General" kg" และตัวเลขแบบจัดกลุ่มคงการแมปเดิม เพราะ Excel ไม่มีฟอร์แมต 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]; // indexer ของ Sheets เริ่มที่ 1
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 ของ Excel อ่านกลับมาไม่มี number format
// ตั้งแต่ v2.384.72 แทนที่จะเป็น '0.00'
Writeln('A2 format: "', Sheet.Cells[2, 1].NumberFormat, '"');
finally
Book.Free;
end;
end;
สูตรของกฎกลับมาเป็นไวยากรณ์ Excel คั่นด้วยคอมมา รูปเดียวกับที่คุณจะส่งเข้า AddCondFormatExpression กฎที่ HotXLS เขียนจึงอ่านกลับมาเป็นสตริงเดิมเป๊ะ เรื่องกว้าง ๆ ว่าเส้นทาง import ODS เก็บอะไรกับทิ้งอะไร ดูที่คู่มือ open กับ save ODS แบบ round-trip ของ HotXLS ส่วนแถว repeated จาก Excel กับ LibreOffice ถูกกางออกอย่างไรตอน import ดูที่แถว repeated ของ ODS ในรูป run ของความสูงแถว
interop ของ conditional format แบบ ODS ใน HotXLS มีขีดจำกัดอะไร
แนวทาง markup ซ้อนสองรูปครอบคลุมกฎเปรียบเทียบค่ากับกฎสูตร และหยุดตรงนั้น ส่วนที่เหลือเขียนให้ฝั่งเดียวหรือไม่เขียนเลย:
- color scale กับ data barถูกเขียนเป็น element calcext เท่านั้น LibreOffice โชว์ได้ แต่ Excel ไม่ได้
- กฎชนิดอื่นอย่าง icon set, กฎข้อความ, top-N, above-average กับกฎค่าซ้ำ ยังไม่มีผลลัพธ์ ODS ใน writer ปัจจุบัน กฎข้อความมักเขียนใหม่เป็นกฎสูตรได้ เช่น
ISNUMBER(SEARCH("late",B2))บนB2:B200ซึ่งงั้นจะถึงทั้งสองแอปพลิเคชัน - กฎแบบทั้งคอลัมน์และทั้งแถวอย่าง
C:Cถูกวางเฉพาะบนพื้นที่ตารางที่เขียนจริง ไม่ใช่บนแถวทั้ง 1,048,576 แถว Excel จึงเห็นกฎพวกนี้เฉพาะเซลล์ที่มีอยู่ในไฟล์ - ไฟล์ที่มีแต่ style:map เมื่อไฟล์ไม่มีบล็อก calcext HotXLS ตีความ reference สัมพัทธ์ในกฎสูตรจากมุมซ้ายบนของช่วงที่สร้างใหม่ ไม่ใช่จากการเลื่อนจาก base cell ที่ระบุไว้
- กฎทับซ้อนจาก LibreOffice เมื่อเซลล์หนึ่งถูกกฎหลายกฎครอบ LibreOffice เขียน map ของกฎแรกเท่านั้นลงไป ไฟล์แบบนั้นอ่านให้ครบจาก
style:mapอย่างเดียวไม่ได้ อีกหนึ่งเหตุผลที่ reader เลือก calcext เมื่อมีทั้งคู่
ขีดจำกัดด้านกระบวนการสำคัญกว่าทุกข้อข้างบน ข้อบกพร่องหลังรีลีสพวกนี้รอดผ่าน round trip ที่เขียน ODS แล้วอ่านกลับด้วย HotXLS มาได้ และบางเรื่องจะรอดผ่านการเช็กมือในแอปผิดฝั่งด้วย สูตรทั้งคอลัมน์ทำงานได้ใน LibreOffice ขณะที่ Excel โชว์ #NAME? และตั้งแต่ v2.384.66 กฎสูตรทำงานได้ใน LibreOffice ขณะที่ Excel ยังไม่โชว์กฎเลยจนถึง v2.384.69 ถ้า ODS interop เป็นข้อบังคับ เทสต์ตอบรับคือเปิดไฟล์ใน Excel และใน LibreOffice แล้วเทียบว่าแต่ละตัวโชว์อะไร วินัยเดียวกันใช้กับสไตล์ที่กฎชี้ไปหาด้วย บทความ conditional formatting กับ styles ของ HotXLS เล่าว่าสไตล์ไฮไลต์ถูกนิยามฝั่ง workbook อย่างไร
สรุป: ODS ที่ทั้งสองแอปพลิเคชันอ่านได้
- ประกาศ
xmlns:ofกับxmlns:msoxlที่ root ของcontent.xmlไม่งั้น LibreOffice โชว์#VALUE!ให้ทุกสูตร (HotXLS ตั้งแต่ v2.384.56) - เขียน reference เป็น
[.A1]เก็บ$ทุกตัว และเขียนคอลัมน์กับแถวทั้งชุดเป็น[.A:.A]กับ[.1:.1](ตั้งแต่ v2.384.55 กับ v2.384.65) - ใช้
;คั่นอาร์กิวเมนต์~คั่น union ของ reference และ|ระหว่างแถวของ array แบบ inline - เขียนกฎค่าหรือกฎสูตรแต่ละกฎเป็น
<style:map>บนสไตล์ของทุกเซลล์ที่ครอบ เพื่อ Excel และเป็นเงื่อนไข calcext เพื่อ LibreOffice (ตั้งแต่ v2.384.69) - ใน calcext วาง operator ไว้ในค่า (
>3,between(1,10)) และสะกดกฎสูตรว่าformula-is(...)พร้อม base cell (ตั้งแต่ v2.384.66 กับ v2.384.69) - คาดหมายว่า number style แบบ General ที่ไม่มี
number:decimal-placesจะเจอบนตอน import HotXLS อ่านมันเป็น General ตั้งแต่ v2.384.72 - ตรวจ export profile ใหม่ทุกอันด้วยการเปิดไฟล์ทั้งใน Excel และ LibreOffice อย่าเปิดแค่ฝั่งเดียวเด็ดขาด
HotXLS เป็นไลบรารี spreadsheet แท้ของ Delphi กับ C++Builder ที่อ่านและเขียน XLS, XLSX กับ ODS ได้โดยไม่ต้องติดตั้ง Excel หรือ LibreOffice ซอร์สเต็ม รายการฟีเจอร์ และการลิขสิทธิ์อยู่ที่หน้า HotXLS Delphi spreadsheet component