En Delphi-rapportbackend som i åratal har skrivit ut .xlsx får ett nytt krav: en kund inom offentlig sektor har upphandlingsregler som föreskriver OpenDocument Spreadsheet-utdata, och analytikerna på det kontot skickar tillbaka sina redigeringar som .ods-filer sparade från LibreOffice. Så nu måste samma kod skriva ODS och läsa den. HotXLS, losLabs nativa Object Pascal-kalkylbibliotek för Delphi och C++Builder, hanterar båda riktningarna utan att vare sig Excel eller LibreOffice är installerat någonstans. Vad det inte gör är att göra de två riktningarna symmetriska. Export bär med sig långt mer än vad import återvinner, och ett team som antar motsatsen kommer att se formler och formatering avdunsta någonstans mellan kundens revidering och nästa rapport, utan något fel att peka på
ODS-stöd bor på XLSX-fasaden, inte på XLS-fasaden
HotXLS levererar två oberoende klasshierarkier i ett enda paket: TXLSWorkbook i enheten lxHandle för binära BIFF8 .xls-filer, och TXLSXWorkbook i enheten lxHandleX för OOXML .xlsx-paket. Varje OpenDocument-ingångspunkt - OpenODS, SaveAsODS, GetODSSheetNames - hänger på TXLSXWorkbook. Placeringen är inte godtycklig. Ett ODS-paket, som specificerat i OASIS ODF 1.3, är ett zip-arkiv som bär en mimetype-medlem, en manifest och en content.xml-kropp, vilket gör det till en strukturell kusin till OOXML-zippen; BIFF8 är en binär postström från 1990-talet utan något gemensamt
Den placeringen har en praktisk baksida: en äldre .xls-arbetsbok kan inte bli .ods i ett enda anrop. Du bygger en bro för BIFF-innehållet in i XLSX-modellen först, med SaveXLSWorkbookAsXLSX från enheten lxXlsxExport, öppnar resultatet igen via TXLSXWorkbook, och exporterar sedan därifrån. Bron är inte förlustfri, och det är värt att känna till glappen innan du bygger på den. Den kopierar värden, formler, talformat, typsnitt, fyllningar och kolumnbredder. Den släpper ramlinjer, sammanslagna områden, kommentarer, diagram och villkorlig formatering. En .xls-källa med tung formatering kommer att nå ODS och se mer avskalad ut än när den lämnade, och det är en egenskap hos bron, inte hos ODS-skrivaren
Detektering på importsidan är automatisk. Den enkla metoden Open känner igen ett ODS-paket via dess mimetype-medlem, och faller tillbaka till en kontroll av en content.xml på toppnivå när den medlemmen saknas, så en generisk "öppna vad användaren än laddade upp"-kodväg behöver ingen egen filändelsedetektering. Efter öppningen rapporterar egenskapen SourceFormat vilken gren som utlöstes
Att exportera till ODS med TODSExportOptions
Själva exportanropet är en rad; options-objektet runt det bär besluten en granskare kommer att fråga om senare:
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // anroparen äger och frigör detta
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 ägs av anroparen. HotXLS kommer inte att frigöra det, vilket är varför den inre try..finally finns och inte är valfri. De två egenskaperna som ändrar utdatan, snarare än att bara märka den, förtjänar en närmare titt. Att sätta IncludeCharts := False gör mer än att dölja diagram: det tar bort diagramunderdokumenten och deras manifestposter ur paketet, vilket är precis vad du vill när mottagaren är en datapipeline som skulle snubbla på dem. Generator åsidosätter ODF-strängen meta:generator, som annars lyder HotXLS/<version>; åsidosätt den när nedströmsverktyg fingeravtrycksidentifierar filproducenter för att styra support. Om inget av det gäller, hoppa över options-objektet helt. Att anropa SaveAs(FileName, xlsxOpenDocumentSpreadsheet) är samma sak som SaveAsODS med standardvärden, och strömöverlagringar på båda låter dig skriva paketet direkt in i ett HTTP-svar utan någon temporär fil
Vad importvägen läser - och vad den avsiktligt hoppar över
Läs den här delen noga innan du lovar någon fidelitet för tur och retur. ODS-import i HotXLS är avsiktligt en lättviktig väg. Den bevarar skalära cellvärden och det cachade resultatet varje formel bar vid sparningstillfället, och den expanderar upprepade rader och kolumner ut i rutnätet. Den för inte över stilar, ODS-formeluttryck eller ritningar
Formelvalet är det som mest sannolikt biter, och det gjordes med avsikt. En ODF-cell lagrar två saker sida vid sida: formeluttrycket, skrivet i OpenFormula-dialekten definierad i ODF 1.3 Part 4, och det senaste värdet det producerande programmet beräknade för den. Att översätta OpenFormula till Excels formelsyntax är sitt eget dialektkonverteringsproblem, med verkliga gränsfall kring funktionsvokabulärer, referenssyntax och felmodeller. Att läsa det cachade värdet i stället kringgår hela den klassen av tyst felöversättning, så talen du importerar är exakt de tal avsändaren senast såg. Kostnaden är att de anländer som tal, inte som de levande formlerna som producerade dem
Felbeteendet att designa kring följer direkt: ett kalkylblad vars summor var korrekta när LibreOffice senast sparade det importeras med korrekta tal, men de talen är nu konstanter. Redigera en indatacell, beräkna om, och ingenting rör sig - formeln är borta, bara dess slutresultat kvarstår. Om arbetsflödet behöver levande formler efter import, återupprätta dem programmatiskt utifrån dina egna affärsregler via Cell.Formula, som på XLSX-fasaden tar uttrycket utan ett inledande likhetstecken
Att designa kring den asymmetriska tur-och-retur-resan
Export renderar från den fullständiga arbetsboksmodellen i minnet: värden, stilar och, om du ber om dem, diagram och bilder. Import returnerar bara värden. Så steget .xlsx till .ods har hög fidelitet, och steget .ods till .xlsx tar med sig värden och cachade resultat men ingen styling och inga levande formler. Kedja ihop de två och asymmetrin förstärks. En fullständig cykel .xlsx till .ods till .xlsx skriver allt troget på vägen ut och förlorar stilarna och formlerna på vägen tillbaka in, trots att inget gick fel i något steg
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // format autodetekterat
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// Värden och cachade formelresultat finns efter en ODS-
// import; stilar och levande formler gör det inte. Återbygg vad
// nedströmspipelinen än beror på innan sparning.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
Arkitekturmönstret som faller ut av detta: behandla inkommande .ods-filer som dataflöden, inte som dokument att redigera på plats. Håll den kanoniska arbetsboken i .xlsx, läs ut värden ur kundrevideringar, och skriv ut färsk ODS på begäran från den kanoniska kopian. Verifiering hör hemma i båda lägren - öppna exporterade filer i LibreOffice Calc, referens-ODF-konsumenten, och i Excel, som har läst ODS i flera år men är oense med LibreOffice vid kanterna av diagram- och stilstöd. Bladantal, en handfull nyckelceller och diagramnärvaro utgör en tillräcklig rökkontroll per exportprofil
Att triagera en ODS-fil innan du åtar dig en import
När en ändpunkt tar emot uppladdningar är det att lista bladnamn långt billigare än en fullständig tolkning och fångar strukturella överraskningar tidigt:
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;
Returkonventionen får folk att snubbla: HotXLS-anrop returnerar generellt ett positivt antal eller 1 vid framgång och -1 vid misslyckande, och tömmer listan när de misslyckas, så testa <= 0 snarare än att jämföra mot ett specifikt positivt värde. GetODSSheetNames varken återställer eller fyller i arbetsboksinstansen, så ett enda testobjekt kan granska en hel katalog med inkommande filer. Strukturella kontroller som den här fångar det vanligaste verkliga felet - en analytiker som byter namn på eller tar bort ett blad innan de skickar tillbaka revideringen - vid grinden, där felmeddelandet fortfarande kan namnge filen och det saknade bladet i stället för att dyka upp som en nil-referens tre lager djupare
Om du bygger en bredare konverteringspipeline runt det här visar mönstret för arbetsboksgranskning och konverteringsverkstad hur man inventerar en fils funktioner innan man väljer ett målformat, och guiden om prestanda för stora arbetsböcker håller batchexporter inom förnuftiga minnesgränser
HotXLS är ett nativt kalkylbibliotek för Delphi och C++Builder med fullständig källkod; den fullständiga funktionslistan och licensdetaljerna finns på produktsidan för HotXLS Delphi Component