Teknisk artikel

Åbning og lagring af ODS-regneark i Delphi med HotXLS

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

Diagram over HotXLS-klasselayoutet i Delphi, hvor hvert ODS-indgangspunkt bor på TXLSXWorkbook, og en SaveXLSWorkbookAsXLSX-bro bærer BIFF8 .xls-indhold over
Hvert OpenDocument-indgangspunkt hænger på TXLSXWorkbook, og en ældre .xls når ODS kun gennem den tabsfulde BIFF-til-XLSX-bro

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

Diagram over den asymmetriske HotXLS ODS round trip fra Delphi: full-fidelity-eksport fra den in-memory projektmappemodel og en values-only-import, der efterlader formler som konstanter
Eksport renderer den fulde in-memory-model, mens import returnerer værdier og cachede resultater, så en fuld .xlsx-til-.ods-til-.xlsx-cyklus dropper stilarter og levende formler i stilhed
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:

Diagram over HotXLS upload triage-port i Delphi, hvor GetODSSheetNames afviser ulæselige ODS-pakker og manglende ark, før en fuld import kører
En GetODSSheetNames-probe koster langt mindre end en fuld parse og opdager fejlen med omdøbt ark, mens fejlen stadig kan nævne filen
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