Technisch artikel

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

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

Diagram van de HotXLS-klasselay-out in Delphi waarin elk ODS-ingangspunt op TXLSXWorkbook leeft en een SaveXLSWorkbookAsXLSX-brug BIFF8 .xls-content overdraagt
Elk OpenDocument-entrypunt hangt aan TXLSXWorkbook, en een legacy .xls bereikt ODS alleen via de verlieslatende BIFF-naar-XLSX-brug

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

Diagram van de asymmetrische HotXLS ODS-roundtrip vanuit Delphi: export met volledige getrouwheid vanuit het in-memory werkboekmodel en een import met alleen waarden die formules als constanten achterlaat
Export rendert het volledige in-memory model terwijl import waarden en gecachte resultaten teruggeeft, dus een volledige .xlsx-naar-.ods-naar-.xlsx-cyclus laat stilletjes stijlen en levende formules vallen
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:

Diagram van de HotXLS-uploadtriagepoort in Delphi waarin GetODSSheetNames onleesbare ODS-pakketten en ontbrekende bladen weigert voordat een volledige import draait
Een GetODSSheetNames-probe kost veel minder dan een volledige parse en vangt de hernoemd-sheet-fout terwijl de fout het bestand nog kan benoemen
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