Műszaki cikk

ODS fájlok megnyitása és mentése Delphiben a HotXLS-szel

Egy Delphi jelentéskiszolgáló, amely évek óta .xlsx fájlokat bocsát ki, új követelményt kap: egy közszférás ügyfél beszerzési szabályai OpenDocument táblázatkimenetet írnak elő, az ügyfélhez rendelt elemzők pedig LibreOffice-ból mentett .ods fájlokban küldik vissza a szerkesztéseiket. Így most ugyanannak a kódnak ODS-t kell írnia és olvasnia. A HotXLS, a losLab natív Object Pascal táblázatkezelő könyvtára Delphihez és C++Builderhez, mindkét irányt kezeli anélkül, hogy bárhol telepített Excel vagy LibreOffice lenne. Amit nem tesz, az a két irány szimmetrikussá tétele. Az export sokkal többet visz, mint amennyit az import visszanyer, és az a csapat, amely mást feltételez, végig fogja nézni, ahogy a képletek és a formázás elpárolognak valahol az ügyfél átdolgozása és a következő jelentés között, minden hiba nélkül, amelyre mutathatna

Az ODS támogatás az XLSX homlokzaton él, nem az XLS-en

A HotXLS két független osztályhierarchiát szállít egyetlen csomagban: a TXLSWorkbook osztályt az lxHandle unitban a bináris BIFF8 .xls fájlokhoz, és a TXLSXWorkbook osztályt az lxHandleX unitban az OOXML .xlsx csomagokhoz. Minden OpenDocument belépési pont - az OpenODS, a SaveAsODS, a GetODSSheetNames - a TXLSXWorkbook osztályról lóg le. Az elhelyezés nem önkényes. Az ODS csomag, ahogy az OASIS ODF 1.3 meghatározza, olyan zip archívum, amely mimetype tagot, egy jegyzéket és egy content.xml törzset hordoz, ami szerkezeti unokatestvérré teszi az OOXML zip csomaggal; a BIFF8 ezzel szemben egy 1990-es évekbeli bináris rekordfolyam, amelynek semmi köze hozzá

Ennek az elhelyezésnek gyakorlati éle van: egy örökölt .xls munkafüzet nem válhat .ods fájllá egyetlen hívással. Előbb átvezeti a BIFF tartalmat az XLSX modellbe az lxXlsxExport unit SaveXLSWorkbookAsXLSX függvényével, újranyitja az eredményt a TXLSXWorkbook osztályon át, majd onnan exportál. A híd nem veszteségmentes, és érdemes ismerni a réseit, mielőtt rá épít. Átmásolja az értékeket, képleteket, számformátumokat, betűtípusokat, kitöltéseket és oszlopszélességeket. Elejti a szegélyeket, egyesített tartományokat, megjegyzéseket, diagramokat és feltételes formázást. Egy erősen formázott .xls forrás csupaszabban érkezik ODS-be, mint ahogy elindult, és ez a híd tulajdonsága, nem az ODS íróé

Az import oldalon a felismerés automatikus. Az egyszerű Open metódus a mimetype tagja alapján ismeri fel az ODS csomagot, és a felső szintű content.xml ellenőrzésére esik vissza, ha az a tag hiányzik, így egy általános „nyisd meg, amit a felhasználó feltöltött” kódútvonalnak nincs szüksége saját kiterjesztésszimatolásra. Megnyitás után a SourceFormat tulajdonság közli, melyik ág sült el

Ábra a HotXLS osztályelrendezéséről Delphiben, ahol minden ODS belépési pont a TXLSXWorkbook osztályon él, és egy SaveXLSWorkbookAsXLSX híd viszi át a BIFF8 .xls tartalmat
Minden OpenDocument belépési pont a TXLSXWorkbook osztályról lóg le, egy örökölt .xls pedig csak a veszteséges BIFF-ből XLSX-be vezető hídon át jut ODS-be

Export ODS-be a TODSExportOptions használatával

Maga az exporthívás egysoros; a köré épülő beállításobjektum hordozza azokat a döntéseket, amelyekről egy átvizsgáló később kérdezni fog:

var
  Book: TXLSXWorkbook;
  Opts: TODSExportOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarterly-report.xlsx');
    Opts := TODSExportOptions.Create;        // ezt a hívó birtokolja és szabadítja fel
    try
      Opts.Generator := 'ReportService 4.2'; // a meta:generator felülírása
      Opts.IncludeCharts := True;
      Opts.IncludeImages := True;
      Book.SaveAsODS('quarterly-report.ods', Opts);
    finally
      Opts.Free;
    end;
  finally
    Book.Free;
  end;
end;

A beállításobjektumot a hívó birtokolja. A HotXLS nem szabadítja fel, ezért van ott a belső try..finally, és ezért nem elhagyható. Az a két tulajdonság, amely a kimenetet is megváltoztatja, nem csak felcímkézi, közelebbi pillantást érdemel. Az IncludeCharts := False beállítása többet tesz a diagramok elrejtésénél: kiszedi a diagram-aldokumentumokat és jegyzékbejegyzéseiket a csomagból, és pontosan ez kell akkor, amikor a fogyasztó olyan adatfolyamat, amely megbotlana bennük. A Generator felülírja az ODF meta:generator karakterláncot, amely egyébként HotXLS/<version> alakban áll; írja felül, ha az alsóbb eszközök a fájlok előállítóját ujjlenyomatozzák a támogatás irányításához. Ha ezek egyike sem érvényes, hagyja ki teljesen a beállításobjektumot. A SaveAs(FileName, xlsxOpenDocumentSpreadsheet) hívás ugyanaz, mint a SaveAsODS alapértelmezésekkel, a mindkettőn meglévő adatfolyamos túlterhelések pedig lehetővé teszik, hogy a csomagot ideiglenes fájl nélkül, egyenesen egy HTTP válaszba írja

Mit olvas be az importút - és mit hagy ki szándékosan

Ezt a részt figyelmesen olvassa, mielőtt bárkinek oda-vissza hűséget ígér. Az ODS import a HotXLS-ben szándékosan könnyű útvonal. Megőrzi a skalár cellaértékeket és azt a gyorsítótárazott eredményt, amelyet az egyes képletek mentéskor hordoztak, az ismételt sorokat és oszlopokat pedig kibontja a rácsba. Nem hozza át a stílusokat, az ODS képletkifejezéseket és a rajzokat

A képletekre vonatkozó döntés az, amely a legvalószínűbben harap, és szándékosan született. Egy ODF cella két dolgot tárol egymás mellett: a képletkifejezést, amely az ODF 1.3 4. részében meghatározott OpenFormula nyelvjárásban íródott, és azt az utolsó értéket, amelyet az előállító alkalmazás kiszámolt rá. Az OpenFormula átfordítása Excel képletszintaxisra önálló nyelvjárás-átalakítási feladat, valódi határesetekkel a függvényszókincsek, a hivatkozásszintaxis és a hibamodellek körül. A gyorsítótárazott érték beolvasása helyette megkerüli a néma félrefordítás egész osztályát, így a beimportált számok pontosan azok a számok, amelyeket a küldő utoljára látott. Ennek ára az, hogy számként érkeznek, nem az őket előállító élő képletekként

Az a hibamód, amely köré tervezni kell, ebből közvetlenül következik: az a táblázat, amelynek összegei helyesek voltak, amikor a LibreOffice utoljára mentette, helyes számokkal importálódik, de azok a számok most már konstansok. Szerkesszen egy bemeneti cellát, számoltasson újra, és semmi nem mozdul - a képlet elment, csak a végeredménye maradt. Ha a munkafolyamatnak import után élő képletekre van szüksége, programozottan állítsa vissza őket a saját üzleti szabályaiból a Cell.Formula tagon át, amely az XLSX homlokzaton kezdő egyenlőségjel nélkül veszi a kifejezést

Tervezés az aszimmetrikus oda-vissza út köré

Az export a teljes, memóriában lévő munkafüzetmodellből jelenít meg: értékeket, stílusokat, és ha kéri, diagramokat és képeket. Az import csak értékeket ad vissza. Így az .xlsx-ből .ods-be vezető szakasz nagy hűségű, az .ods-ből .xlsx-be vezető pedig értékeket és gyorsítótárazott eredményeket hoz vissza, stílusok és élő képletek nélkül. Fűzze össze a kettőt, és az aszimmetria összeadódik. Egy teljes .xlsx - .ods - .xlsx kör kifelé menet mindent hűen kiír, befelé menet pedig elveszíti a stílusokat és a képleteket, jóllehet egyik lépésnél sem történt semmi baj

Ábra a HotXLS aszimmetrikus ODS oda-vissza útjáról Delphiből: teljes hűségű export a memóriában lévő munkafüzetmodellből és csak értékeket hozó import, amely konstansként hagyja a képleteket
Az export a teljes memóriabeli modellt jeleníti meg, míg az import értékeket és gyorsítótárazott eredményeket ad vissza, így egy teljes .xlsx - .ods - .xlsx kör csendben elejti a stílusokat és az élő képleteket
Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // a formátum automatikusan felismerve
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // Az ODS import után az értékek és a gyorsítótárazott képlet-
    // eredmények jelen vannak; a stílusok és az élő képletek nem.
    // Mentés előtt építse újra, amire az alsóbb folyamat épül.
    Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
    Book.SaveAs('vendor-revision.xlsx');
  end;
finally
  Book.Free;
end;

Az ebből kihulló építészeti minta: a beérkező .ods fájlokat adatfolyamként kezelje, ne helyben szerkesztendő dokumentumként. Tartsa a mérvadó munkafüzetet .xlsx formátumban, olvassa ki az értékeket az ügyfél átdolgozásaiból, és igény szerint bocsásson ki friss ODS fájlt a mérvadó példányból. Az ellenőrzés mindkét táborba tartozik - nyissa meg az exportált fájlokat LibreOffice Calc programban, amely a referencia ODF fogyasztó, és Excelben, amely évek óta olvas ODS-t, de a diagram- és stílustámogatás peremén nem ért egyet a LibreOffice-szal. A lapszám, néhány kulcscella és a diagramok megléte elegendő füstpróba exportprofilonként

Egy ODS fájl osztályozása az import elvállalása előtt

Amikor egy végpont feltöltéseket fogad, a lapnevek listázása jóval olcsóbb a teljes elemzésnél, és korán elkapja a szerkezeti meglepetéseket:

Ábra a HotXLS feltöltés-osztályozó kapujáról Delphiben, ahol a GetODSSheetNames a teljes import lefutása előtt utasítja el az olvashatatlan ODS csomagokat és a hiányzó lapokat
Egy GetODSSheetNames próba sokkal kevesebbe kerül a teljes elemzésnél, és akkor kapja el az átnevezett lap okozta hibát, amikor a hibaüzenet még meg tudja nevezni a fájlt
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;

A visszatérési egyezmény megbuktatja az embereket: a HotXLS hívásai általában pozitív darabszámot vagy sikerkor 1-et, hibakor -1-et adnak vissza, és hiba esetén ürítik a listát, ezért a <= 0 feltételt vizsgálja, ne egyetlen konkrét pozitív értékkel hasonlítson. A GetODSSheetNames sem nem állítja alaphelyzetbe, sem nem tölti fel a munkafüzet-példányt, így egyetlen próbaobjektum átvizsgálhat egy egész könyvtárnyi beérkező fájlt. Az ilyen szerkezeti ellenőrzések a leggyakoribb valós hibát - hogy egy elemző átnevez vagy töröl egy lapot, mielőtt visszaküldi az átdolgozást - a kapunál kapják el, ahol a hibaüzenet még meg tudja nevezni a fájlt és a hiányzó lapot ahelyett, hogy három réteggel mélyebben nil hivatkozásként bukkanna fel

Ha ez köré szélesebb átalakítási folyamatot épít, a munkafüzet-ellenőrző és átalakító műhely mintája megmutatja, hogyan vegye leltárba egy fájl képességeit a célformátum kiválasztása előtt, a nagy munkafüzetek teljesítményéről szóló útmutató pedig józan memóriakorlátok között tartja a kötegelt exportokat

A HotXLS natív Delphi és C++Builder táblázatkezelő könyvtár teljes forráskóddal; a teljes képességlista és a licencelési részletek a HotXLS Delphi Component termékoldalán találhatók