Een Delphi-rapportagebackend die al jaren .xlsx uitzendt, krijgt een nieuwe eis: de inkoopregels van een klant uit de publieke sector schrijven OpenDocument Spreadsheet-uitvoer voor, en de analisten van dat account sturen hun bewerkingen terug als .ods-bestanden opgeslagen vanuit LibreOffice. Dus nu moet dezelfde code ODS schrijven én lezen. HotXLS, losLabs native Object Pascal-spreadsheetbibliotheek voor Delphi en C++Builder, behandelt beide richtingen zonder dat er ergens Excel of LibreOffice geïnstalleerd hoeft te zijn. Wat het niet doet, is de twee richtingen symmetrisch maken. Export draagt veel meer over dan import terugkrijgt, en een team dat het tegendeel aanneemt, zal formules en opmaak zien verdampen ergens tussen de revisie van de klant en het volgende rapport, zonder een fout om naar te wijzen
ODS-ondersteuning zit op de XLSX-facade, niet de XLS-facade
HotXLS levert twee onafhankelijke klassenhiërarchieën in één pakket: TXLSWorkbook in unit lxHandle voor binaire BIFF8-.xls-bestanden, en TXLSXWorkbook in unit lxHandleX voor OOXML-.xlsx-pakketten. Elk OpenDocument-toegangspunt - OpenODS, SaveAsODS, GetODSSheetNames - hangt aan TXLSXWorkbook. Die plaatsing is niet willekeurig. Een ODS-pakket, zoals gespecificeerd in OASIS ODF 1.3, is een zip-archief met een mimetype-lid, een manifest en een content.xml-body, wat het een structurele neef van de OOXML-zip maakt; BIFF8 is een binaire recordstroom uit de jaren negentig zonder iets gemeenschappelijks
Die plaatsing heeft een praktische scherpe kant: een legacy .xls-werkmap kan niet in één aanroep .ods worden. Je overbrugt de BIFF-inhoud eerst naar het XLSX-model, met SaveXLSWorkbookAsXLSX uit unit lxXlsxExport, heropent het resultaat via TXLSXWorkbook, en exporteert dan vandaar. De brug is niet verliesvrij, en het is de moeite waard de gaten te kennen voordat je erop bouwt. Hij kopieert waarden, formules, getalnotaties, lettertypen, vullingen en kolombreedtes. Hij laat randen, samengevoegde bereiken, opmerkingen, grafieken en voorwaardelijke opmaak vallen. Een .xls-bron met zware opmaak komt kaler bij ODS aan dan hij vertrok, en dat is een eigenschap van de brug, niet van de ODS-schrijver
Detectie aan de importkant is automatisch. De gewone Open-methode herkent een ODS-pakket aan zijn mimetype-lid, met een terugval naar een controle van een top-level content.xml wanneer dat lid ontbreekt, dus een generiek codepad "open wat de gebruiker ook heeft geüpload" heeft geen eigen extensiedetectie nodig. Na het openen meldt de eigenschap SourceFormat welke tak is geactiveerd
Exporteren naar ODS met TODSExportOptions
De exportaanroep zelf is één regel; het opties-object eromheen draagt de beslissingen waar een reviewer later naar zal vragen:
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // aanroeper bezit dit en geeft het vrij
try
Opts.Generator := 'ReportService 4.2'; // meta:generator-override
Opts.IncludeCharts := True;
Opts.IncludeImages := True;
Book.SaveAsODS('quarterly-report.ods', Opts);
finally
Opts.Free;
end;
finally
Book.Free;
end;
end;
Het opties-object is eigendom van de aanroeper. HotXLS geeft het niet vrij, en daarom is de binnenste try..finally aanwezig en niet optioneel. De twee eigenschappen die de uitvoer daadwerkelijk veranderen, in plaats van hem alleen te labelen, verdienen een nadere blik. IncludeCharts := False instellen doet meer dan grafieken verbergen: het strippt de grafieksubdocumenten en hun manifestitems uit het pakket, wat precies is wat je wilt wanneer de consument een datapipeline is die erover zou struikelen. Generator overschrijft de ODF-meta:generator-string, die anders HotXLS/<version> luidt; overschrijf hem wanneer downstream-tooling bestandsproducenten fingerprint om support te routeren. Als niets daarvan van toepassing is, sla het opties-object volledig over. SaveAs(FileName, xlsxOpenDocumentSpreadsheet) aanroepen is hetzelfde als SaveAsODS met standaardwaarden, en stream-overloads op beide laten je het pakket rechtstreeks in een HTTP-respons schrijven zonder tijdelijk bestand
Wat het importpad leest - en wat het bewust overslaat
Lees dit deel zorgvuldig voordat je iemand round-trip-getrouwheid belooft. ODS-import in HotXLS is bewust een lichtgewicht pad. Het behoudt scalaire celwaarden en het gecachte resultaat dat elke formule op opslagmoment droeg, en het expandeert herhaalde rijen en kolommen uit in het raster. Het brengt geen stijlen, ODS-formule-expressies of tekeningen over
De formulekeuze is degene die het meest waarschijnlijk bijt, en die is met opzet gemaakt. Een ODF-cel slaat twee dingen naast elkaar op: de formule-expressie, geschreven in het OpenFormula-dialect gedefinieerd in ODF 1.3 Deel 4, en de laatste waarde die de producerende applicatie ervoor heeft berekend. OpenFormula vertalen naar Excel-formulesyntax is een eigen dialectconversieprobleem, met echte randgevallen rond functiewoordenschat, verwijzingssyntax en foutmodellen. In plaats daarvan de gecachte waarde lezen omzeilt die hele klasse van stille verkeerde vertaling, dus de getallen die je importeert zijn precies de getallen die de afzender voor het laatst zag. De kosten zijn dat ze aankomen als getallen, niet als de levende formules die ze produceerden
Het faalscenario om omheen te ontwerpen volgt rechtstreeks: een spreadsheet waarvan de totalen correct waren toen LibreOffice hem voor het laatst opsloeg, importeert met correcte getallen, maar die getallen zijn nu constanten. Bewerk een invoercel, herbereken, en er beweegt niets - de formule is verdwenen, alleen het eindresultaat blijft over. Als de workflow levende formules nodig heeft na import, herstel ze dan programmatisch vanuit je eigen bedrijfsregels via Cell.Formula, dat op de XLSX-facade de expressie zonder voorafgaand isgelijkteken neemt
Ontwerpen rond de asymmetrische round trip
Export rendert vanuit het volledige in-memory workbook-model: waarden, stijlen en, als je erom vraagt, grafieken en afbeeldingen. Import geeft alleen waarden terug. Dus het traject .xlsx naar .ods heeft hoge getrouwheid, en het traject .ods naar .xlsx brengt waarden en gecachte resultaten terug maar geen opmaak en geen levende formules. Ketel de twee aaneen en de asymmetrie stapelt zich op. Een volledige cyclus .xlsx naar .ods naar .xlsx schrijft alles getrouw op de heenweg en verliest de stijlen en formules op de terugweg, ook al is er bij geen van beide stappen iets fout gegaan
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // formaat automatisch gedetecteerd
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// Waarden en gecachte formuleresultaten zijn aanwezig na een ODS-
// import; stijlen en levende formules niet. Herbouw waar
// de downstream pipeline van afhangt vóórdat je opslaat.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
Het architecturale patroon dat hieruit voortvloeit: behandel binnenkomende .ods-bestanden als datafeeds, niet als documenten om ter plekke te bewerken. Houd de canonieke werkmap in .xlsx, lees waarden uit klantrevisies, en genereer verse ODS op aanvraag vanuit de canonieke kopie. Verificatie hoort thuis in beide kampen - open geëxporteerde bestanden in LibreOffice Calc, de referentie-ODF-consument, en in Excel, dat al jaren ODS leest maar aan de randen van grafiek- en stijlondersteuning van LibreOffice verschilt. Bladaantal, een handvol sleutelcellen en de aanwezigheid van grafieken vormen een voldoende smoke-check per exportprofiel
Een ODS-bestand triageren vóórdat je aan een import commit
Wanneer een endpoint uploads accepteert, is het opsommen van bladnamen veel goedkoper dan een volledige parse en vangt het structurele verrassingen vroeg op:
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;
De returnconventie doet mensen struikelen: HotXLS-aanroepen geven doorgaans een positief aantal of 1 terug bij succes en -1 bij falen, waarbij de lijst wordt geleegd bij falen, dus test op <= 0 in plaats van te vergelijken met één specifieke positieve waarde. GetODSSheetNames reset noch vult de workbook-instantie, dus één probe-object kan een hele map met binnenkomende bestanden keuren. Structurele controles zoals deze vangen de meest voorkomende praktijkfout - een analist die een blad hernoemt of verwijdert voordat hij de revisie terugstuurt - af bij de poort, waar de foutmelding nog steeds het bestand en het ontbrekende blad kan noemen in plaats van drie lagen dieper op te duiken als een nil-referentie
Als je hier een bredere conversiepipeline omheen bouwt, laat het patroon voor de workbook-audit- en conversiewerkbank zien hoe je de functies van een bestand inventariseert voordat je een doelformaat kiest, en de handleiding voor prestaties van grote werkmappen houdt batch-exports binnen redelijke geheugengrenzen
HotXLS is een native spreadsheetbibliotheek voor Delphi en C++Builder met volledige broncode; de complete functielijst en licentiedetails staan op de productpagina van HotXLS Delphi Component