Technical Article

Otvaranje i spremanje ODS tablica u Delphiju uz HotXLS

Delphi izvještajni backend koji godinama emitira .xlsx dobiva novi zahtjev: pravila javne nabave klijenta iz javnog sektora nalažu izlaz u obliku OpenDocument Spreadsheet (.ods) proračunskih tablica, a analitičari tog klijenta šalju svoje izmjene natrag kao .ods datoteke spremljene iz LibreOfficea. Dakle, sada isti kod mora pisati ODS i čitati ga. HotXLS, nativna Object Pascal biblioteka za proračunske tablice tvrtke losLab za Delphi i C++Builder, obrađuje oba smjera bez potrebe za instaliranim Excelom ili LibreOfficeom bilo gdje na sustavu. Ono što ne čini jest da ta dva smjera ne čini simetričnima. Izvoz prenosi daleko više nego što uvoz vraća, a tim koji pretpostavi drugačije gledat će kako formule i oblikovanje isparavaju negdje između klijentove revizije i sljedećeg izvješća, bez ikakve pogreške koja bi ukazala na to.

Podrška za ODS živi na XLSX sučelju, a ne na XLS sučelju

HotXLS isporučuje dvije neovisne hijerarhije klasa u jednom paketu: TXLSWorkbook u jedinici lxHandle za binarne BIFF8 .xls datoteke, i TXLSXWorkbook u jedinici lxHandleX za OOXML .xlsx pakete. Svaka ulazna točka OpenDocumenta – OpenODS, SaveAsODS, GetODSSheetNames – visi o klasi TXLSXWorkbook. Taj smještaj nije proizvoljan. ODS paket, kako je specificirano u OASIS ODF 1.3, je zip arhiva koja sadrži član mimetype, manifest i tijelo content.xml, što ga čini strukturnim rođakom OOXML zipa; BIFF8 is a binarni tok zapisa iz 1990-ih koji nema ništa zajedničko s tim.

Taj smještaj ima praktičnu prednost: naslijeđena .xls radna knjiga ne može postati .ods u jednom pozivu. Najprije premošćujete BIFF sadržaj u XLSX model pomoću SaveXLSWorkbookAsXLSX iz jedinice lxXlsxExport, ponovno otvarate rezultat kroz TXLSXWorkbook, a zatim izvozite od tamo. Taj most nije bez gubitaka i korisno je znati praznine prije nego što izgradite rješenje na njemu. Kopira vrijednosti, formule, formate brojeva, fontove, ispune i širine stupaca. Odbacuje obrube, spojene raspone, komentare, grafikone i uvjetno oblikovanje. Izvor .xls s teškim oblikovanjem stići će u ODS izgledajući jednostavnije nego što je otišao, a to je svojstvo mosta, a ne pisca ODS-a.

Otkrivanje na strani uvoza je automatsko. Obična metoda Open prepoznaje ODS paket po njegovom članu mimetype, vraćajući se na provjeru content.xml na najvišoj razini kada taj član nije prisutan, tako da generička putanja koda "otvori što god je korisnik prenio" ne treba vlastito njuškanje ekstenzija. Nakon otvaranja, svojstvo SourceFormat javlja koja se grana aktivirala.

Izvoz u ODS s TODSExportOptions

Sam poziv za izvoz je u jednom retku; objekt opcija oko njega nosi odluke o kojima će recenzent kasnije pitati:

var
  Book: TXLSXWorkbook;
  Opts: TODSExportOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarterly-report.xlsx');
    Opts := TODSExportOptions.Create;        // caller owns and frees this
    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;

Objekt opcija je u vlasništvu pozivatelja. HotXLS ga neće osloboditi, zbog čega je unutarnji blok try..finally tu i nije neobavezan. Dva svojstva koja mijenjaju izlaz, a ne samo da ga označavaju, zaslužuju detaljniji pogled. Postavljanje IncludeCharts := False čini više od puke skrivanja grafikona: uklanja poddokumente grafikona i njihove unose manifesta iz paketa, što je upravo ono što želite kada je primatelj cjevovod podataka koji bi se spotaknuo o njih. Generator nadjačava ODF meta:generator niz, koji inače glasi HotXLS/<version>; nadjačajte ga kada nizvodni alati uzimaju otisak prsta proizvođača datoteka za usmjeravanje podrške. Ako se ništa od toga ne primjenjuje, u potpunosti preskočite objekt opcija. Pozivanje SaveAs(FileName, xlsxOpenDocumentSpreadsheet) je isto što i SaveAsODS sa zadanim postavkama, a preopterećenja toka na oba omogućuju vam da paket zapišete izravno u HTTP odgovor bez privremene datoteke.

Što uvozna putanja čita i što namjerno preskače

Pažljivo pročitajte ovaj dio prije nego što ikome obećate dvosmjernu vjernost (round-trip fidelity). Uvoz ODS-a u HotXLS-u je namjerno lagana putanja. Čuva skalarne vrijednosti ćelija i predmemorirani rezultat koji je svaka formula nosila u vrijeme spremanja, te proširuje ponovljene retke i stupce u mrežu. Ne prenosi stilove, ODS izraze formula niti crteže.

Izbor vezan uz formule je onaj koji će vas najvjerojatnije zaskočiti, a donesen je namjerno. ODF ćelija pohranjuje dvije stvari jednu pored druge: izraz formule, zapisan u dijalektu OpenFormula definiranom u ODF 1.3 Dio 4, i posljednju vrijednost koju je aplikacija koja je proizvela datoteku izračunala za nju. Prevođenje OpenFormula u sintaksu Excel formula je vlastiti problem konverzije dijalekata, sa stvarnim graničnim slučajevima oko vokabulara funkcija, sintakse referenci i modela pogrešaka. Umjesto toga, čitanje predmemorirane vrijednosti zaobilazi cijelu tu klasu tihih pogrešnih prijevoda, tako da su brojevi koje uvozite točno oni brojevi koje je pošiljatelj zadnje vidio. Cijena je to što oni stižu kao brojevi, a ne kao aktivne formule koje su ih proizvele.

Način kvara oko kojeg treba dizajnirati rješenje slijedi izravno: proračunska tablica čiji su ukupni iznosi bili točni kada ju je LibreOffice zadnji put spremio uvozi se s točnim brojevima, ali ti su brojevi sada konstante. Uredite ulaznu ćeliju, ponovno izračunajte i ništa se ne miče – formula je nestala, ostaje samo njezin konačni rezultat. Ako tijek rada treba aktivne formule nakon uvoza, ponovno ih uspostavite programski iz vlastitih poslovnih pravila putem Cell.Formula, što na XLSX sučelju prima izraz bez vodećeg znaka jednakosti.

Dizajniranje oko asimetričnog dvosmjernog putovanja

Izvoz se renderira iz cjelovitog modela radne knjige u memoriji: vrijednosti, stilovi i, ako ih zatražite, grafikoni i slike. Uvoz vraća samo vrijednosti. Dakle, dionica od .xlsx do .ods je visoke vjernosti, a dionica od .ods do .xlsx vraća vrijednosti i predmemorirane rezultate, ali bez oblikovanja i bez aktivnih formula. Lančano ih povežite i asimetrija se pojačava. Cijeli ciklus od .xlsx do .ods pa natrag do .xlsx vjerno ispisuje sve na izlasku, a gubi stilove i formule na povratku, iako ništa nije pošlo po zlu ni u jednom koraku.

Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // format auto-detected
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // Values and cached formula results are present after an ODS
    // import; styles and live formulas are not. Rebuild whatever
    // the downstream pipeline depends on before saving.
    Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
    Book.SaveAs('vendor-revision.xlsx');
  end;
finally
  Book.Free;
end;

Arhitektonski uzorak koji proizlazi iz ovoga: tretirajte dolazne .ods datoteke kao izvore podataka, a ne kao dokumente koje treba uređivati na licu mjesta. Držite kanonsku radnu knjigu u .xlsx formatu, čitajte vrijednosti iz klijentovih revizija i emitirajte novi ODS na zahtjev iz kanonske kopije. Provjera pripada obama taborima – otvorite izvezene datoteke u programu LibreOffice Calc, referentnom ODF potrošaču, i u Excelu, koji godinama čita ODS, ali se ne slaže s LibreOfficeom na rubovima podrške za grafikone i stilove. Broj listova, nekoliko ključnih ćelija i prisutnost grafikona čine dovoljnu brzu provjeru po profilu izvoza.

Trijaža ODS datoteke prije uvoza

Kada krajnja točka prihvaća prijenose, ispisivanje naziva listova daleko je jeftinije od potpune analize i rano otkriva strukturna iznenađenja:

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;

Konvencija o povratnim vrijednostima zbunjuje ljude: pozivi u HotXLS-u općenito vraćaju pozitivan broj ili 1 u slučaju uspjeha, a -1 u slučaju neuspjeha, prazneći listu pri neuspjehu, stoga testirajte <= 0 radije nego usporedbu s jednom određenom pozitivnom vrijednošću. GetODSSheetNames niti resetira niti popunjava instancu radne knjige, tako da jedan ispitni objekt može provjeriti cijeli direktorij dolaznih datoteka. Strukturne provjere poput ove rano hvataju najčešći stvarni kvar – analitičara koji preimenuje ili obriše list prije slanja revizije natrag – na samom ulazu, gdje poruka o pogrešci još uvijek može imenovati datoteku i list koji nedostaje, umjesto da se pojavi kao nil referenca tri sloja dublje.

Ako gradite širi cjevovod za konverziju oko ovoga, uzorak revizije radne knjige i konverzijske radne tablice pokazuje kako popisati značajke datoteke prije odabira ciljnog formata, a vodič za performanse velikih radnih knjiga drži skupne izvoze unutar razumnih memorijskih granica.

HotXLS je nativna Delphi i C++Builder biblioteka za proračunske tablice s punim izvornim kodom; cjelovit popis značajki i detalji o licenciranju nalaze se na stranici proizvoda HotXLS komponente.