En Delphi-rapporteringsbackend, der har udsendt .xlsx i årevis, får et nyt krav: en offentlig kundes udbudsregler kræver OpenDocument Spreadsheet-output, og analytikerne på den konto sender deres redigeringer tilbage som .ods-filer gemt fra LibreOffice. Så nu skal den samme kode både skrive ODS og læse den. HotXLS, losLabs native Object Pascal-regnearksbibliotek til Delphi og C++Builder, håndterer begge retninger uden Excel eller LibreOffice installeret noget sted. Det, det ikke gør, er at gøre de to retninger symmetriske. Eksport bærer langt mere, end import genvinder, og et team, der antager andet, vil se formler og formatering fordampe et sted mellem kundens revision og næste rapport, uden nogen fejl at pege på
ODS-understøttelse bor på XLSX-facaden, ikke XLS-facaden
HotXLS leverer to uafhængige klassehierarkier i én pakke: TXLSWorkbook i unitten lxHandle til binære BIFF8 .xls-filer, og TXLSXWorkbook i unitten lxHandleX til OOXML .xlsx-pakker. Hvert OpenDocument-indgangspunkt - OpenODS, SaveAsODS, GetODSSheetNames - hænger på TXLSXWorkbook. Placeringen er ikke tilfældig. En ODS-pakke, som specificeret i OASIS ODF 1.3, er et zip-arkiv, der bærer en mimetype-post, et manifest og en content.xml-krop, hvilket gør den til en strukturel fætter til OOXML-zip'en; BIFF8 er en binær poststrøm fra 1990'erne uden noget til fælles
Den placering har en praktisk skarphed: en gammel .xls-projektmappe kan ikke blive til .ods med ét kald. Man skal først bygge bro for BIFF-indholdet ind i XLSX-modellen, med SaveXLSWorkbookAsXLSX fra unitten lxXlsxExport, genåbne resultatet via TXLSXWorkbook, og derefter eksportere derfra. Broen er ikke tabsfri, og det er værd at kende hullerne, før man bygger videre på den. Den kopierer værdier, formler, taltformater, skrifttyper, udfyldninger og kolonnebredder. Den taber kanter, sammenlagte områder, kommentarer, diagrammer og betinget formatering. En .xls-kilde med tung formatering vil nå ODS og se mere enkel ud, end den forlod, og det er en egenskab ved broen, ikke ved ODS-skriveren
Detektion på importsiden er automatisk. Den almindelige Open-metode genkender en ODS-pakke på dens mimetype-post, med et fald tilbage til et top-niveau content.xml-tjek, når den post mangler, så en generisk "åbn hvad end brugeren uploadede"-kodesti ikke behøver sin egen filendelses-sniffing. Efter åbning rapporterer SourceFormat-egenskaben, hvilken gren der udløste
Eksport til ODS med TODSExportOptions
Selve eksportkaldet er én linje; options-objektet omkring det bærer de beslutninger, en reviewer vil spørge om senere:
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // kalderen ejer og frigør denne
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;
Options-objektet ejes af kalderen. HotXLS vil ikke frigøre det, hvilket er grunden til, at den indre try..finally er der og ikke er valgfri. De to egenskaber, der ændrer output, frem for blot at mærke det, fortjener et nærmere kig. At sætte IncludeCharts := False gør mere end at skjule diagrammer: det strimler diagram-under-dokumenterne og deres manifestposter ud af pakken, hvilket er præcis, hvad man ønsker, når forbrugeren er en datapipeline, der ville snuble over dem. Generator tilsidesætter ODF-meta:generator-strengen, som ellers lyder HotXLS/<version>; tilsidesæt den, når downstream-værktøjer fingeraftrykker filproducenter for at route support. Hvis intet af det gælder, kan man springe options-objektet helt over. At kalde SaveAs(FileName, xlsxOpenDocumentSpreadsheet) er det samme som SaveAsODS med standardindstillinger, og stream-overloads på begge lader dig skrive pakken direkte ind i et HTTP-svar uden temp-fil
Hvad importstien læser - og hvad den bevidst springer over
Læs denne del omhyggeligt, før du lover nogen rundtur-troskab. ODS-import i HotXLS er bevidst en let sti. Den bevarer skalare celleværdier og det cachede resultat, hver formel bar ved gemmetidspunktet, og den udfolder gentagne rækker og kolonner ud i gitteret. Den bringer ikke stilarter, ODS-formeludtryk eller tegninger med over
Formelvalget er det, der mest sandsynligt bider, og det blev truffet med vilje. En ODF-celle gemmer to ting side om side: formeludtrykket, skrevet i OpenFormula-dialekten defineret i ODF 1.3 del 4, og den sidste værdi, det producerende program beregnede for den. At oversætte OpenFormula til Excel-formelsyntaks er sit eget dialekt-konverteringsproblem, med reelle edge cases omkring funktionsvokabularer, referencesyntaks og fejlmodeller. At læse den cachede værdi i stedet omgår hele den klasse af tavs fejloversættelse, så de tal, du importerer, er præcis de tal, afsenderen sidst så. Prisen er, at de ankommer som tal, ikke som de levende formler, der producerede dem
Den fejltilstand, man skal designe omkring, følger direkte: et regneark, hvis totaler var korrekte, da LibreOffice sidst gemte det, importerer med korrekte tal, men de tal er nu konstanter. Redigér en inputcelle, genberegn, og intet flytter sig - formlen er væk, kun dens endelige resultat er tilbage. Hvis workflowet har brug for levende formler efter import, skal man genetablere dem programmatisk fra sine egne forretningsregler via Cell.Formula, som på XLSX-facaden tager udtrykket uden et indledende lighedstegn
At designe omkring den asymmetriske rundtur
Eksport gengiver fra den fulde projektmappe-model i hukommelsen: værdier, stilarter og, hvis man beder om dem, diagrammer og billeder. Import returnerer kun værdier. Så .xlsx-til-.ods-benet er høj-troskab, og .ods-til-.xlsx-benet bringer værdier og cachede resultater tilbage, men ingen styling og ingen levende formler. Kæd de to sammen, og asymmetrien forstærkes. En fuld .xlsx-til-.ods-til-.xlsx-cyklus skriver alt loyalt på vej ud og mister stilarterne og formlerne på vej tilbage ind, selvom intet gik galt i noget af trinene
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // format auto-detekteret
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// Værdier og cachede formelresultater er til stede efter en ODS-
// import; stilarter og levende formler er det ikke. Genopbyg det,
// downstream-pipelinen afhænger af, før gemning.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
Det arkitektoniske mønster, der falder ud af dette: behandl indgående .ods-filer som datafeeds, ikke som dokumenter, der redigeres på stedet. Hold den kanoniske projektmappe i .xlsx, læs værdier ud af kunderevisioner, og udsend frisk ODS på forlangende fra den kanoniske kopi. Verifikation hører hjemme i begge lejre - åbn eksporterede filer i LibreOffice Calc, referenceforbrugeren af ODF, og i Excel, som har læst ODS i årevis, men er uenig med LibreOffice ved kanterne af diagram- og stilunderstøttelse. Arkantal, en håndfuld nøgleceller og diagramtilstedeværelse udgør et tilstrækkeligt smoke-tjek pr. eksportprofil
At triagere en ODS-fil, før man forpligter sig til en import
Når et endpoint accepterer uploads, er det langt billigere at liste arknavne end en fuld parsing, og det fanger strukturelle overraskelser tidligt:
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;
Return-konventionen får folk til at snuble: HotXLS-kald returnerer generelt et positivt antal eller 1 ved succes og -1 ved fejl, og tømmer listen, når de fejler, så test <= 0 frem for at sammenligne mod én specifik positiv værdi. GetODSSheetNames hverken nulstiller eller udfylder projektmappe-instansen, så ét enkelt probe-objekt kan gennemgå en hel mappe af indkommende filer. Strukturelle tjek som dette fanger den mest almindelige virkelige fejl - en analytiker, der omdøber eller sletter et ark, før revisionen sendes tilbage - ved porten, hvor fejlmeddelelsen stadig kan navngive filen og det manglende ark, i stedet for at dukke op som en nil-reference tre lag dybere
Hvis man bygger en bredere konverteringspipeline omkring dette, viser mønstret for projektmappe-audit og konverteringsworkbench, hvordan man inventarerer en fils funktioner, før man vælger et målformat, og guiden til ydeevne for store projektmapper holder batch-eksporter inden for fornuftige hukommelsesgrænser
HotXLS er et nativt Delphi- og C++Builder-regnearksbibliotek med fuld kildekode; den fulde funktionsliste og licensdetaljer findes på HotXLS-Delphi-komponentens produktside