Articol tehnic

Deschiderea și salvarea foilor de calcul ODS în Delphi cu HotXLS

Un backend de raportare Delphi care a emis fișiere .xlsx de ani de zile primește o cerință nouă: regulile de achiziții ale unui client din sectorul public impun ieșire OpenDocument Spreadsheet, iar analiștii de pe acel cont trimit înapoi modificările lor ca fișiere .ods salvate din LibreOffice. Așadar, același cod trebuie acum să scrie ODS și să îl citească. HotXLS, biblioteca nativă Object Pascal pentru foi de calcul a losLab pentru Delphi și C++Builder, gestionează ambele direcții fără ca Excel sau LibreOffice să fie instalate undeva. Ceea ce nu face este să facă cele două direcții simetrice. Exportul transportă mult mai mult decât recuperează importul, iar o echipă care presupune altfel va vedea formule și formatare evaporându-se undeva între revizia clientului și următorul raport, fără nicio eroare la care să se raporteze

Suportul ODS trăiește pe fațada XLSX, nu pe cea XLS

HotXLS livrează două ierarhii de clase independente într-un singur pachet: TXLSWorkbook în unitatea lxHandle pentru fișiere .xls binare BIFF8 și TXLSXWorkbook în unitatea lxHandleX pentru pachete OOXML .xlsx. Fiecare punct de intrare OpenDocument - OpenODS, SaveAsODS, GetODSSheetNames - atârnă de TXLSXWorkbook. Plasarea nu este arbitrară. Un pachet ODS, așa cum este specificat în OASIS ODF 1.3, este o arhivă zip ce poartă un membru mimetype, un manifest și un corp content.xml, ceea ce îl face un văr structural al arhivei OOXML; BIFF8 este un flux de înregistrări binare din anii 1990, fără nimic în comun

Această plasare are un avantaj practic: un registru de lucru .xls vechi nu poate deveni .ods dintr-un singur apel. Mai întâi faceți puntea conținutului BIFF către modelul XLSX, cu SaveXLSWorkbookAsXLSX din unitatea lxXlsxExport, redeschideți rezultatul prin TXLSXWorkbook, apoi exportați de acolo. Puntea nu este lipsită de pierderi, și merită cunoscute lacunele înainte de a construi pe ea. Copiază valori, formule, formate numerice, fonturi, umpluturi și lățimi de coloane. Renunță la borduri, intervale îmbinate, comentarii, diagrame și formatare condiționată. O sursă .xls cu formatare intensă va ajunge în ODS arătând mai simplu decât a plecat, iar aceasta este o proprietate a punții, nu a generatorului ODS

Detecția pe partea de import este automată. Metoda simplă Open recunoaște un pachet ODS după membrul său mimetype, revenind la o verificare content.xml de nivel superior atunci când acel membru lipsește, astfel încât o cale de cod generică de tipul „deschide orice a încărcat utilizatorul” nu are nevoie de propria detectare a extensiei. După deschidere, proprietatea SourceFormat raportează care ramură s-a declanșat

Diagramă a aspectului de clase HotXLS în Delphi, în care fiecare punct de intrare ODS trăiește pe TXLSXWorkbook, iar o punte SaveXLSWorkbookAsXLSX duce conținutul .xls BIFF8 peste
Fiecare punct de intrare OpenDocument atârnă de TXLSXWorkbook, iar un .xls legacy ajunge la ODS doar prin puntea pierzătoare BIFF către XLSX

Exportul către ODS cu TODSExportOptions

Apelul de export în sine este o singură linie; obiectul de opțiuni din jurul lui poartă deciziile despre care un recenzent va întreba mai târziu:

var
  Book: TXLSXWorkbook;
  Opts: TODSExportOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarterly-report.xlsx');
    Opts := TODSExportOptions.Create;        // apelantul deține și eliberează acest obiect
    try
      Opts.Generator := 'ReportService 4.2'; // suprascrie meta:generator
      Opts.IncludeCharts := True;
      Opts.IncludeImages := True;
      Book.SaveAsODS('quarterly-report.ods', Opts);
    finally
      Opts.Free;
    end;
  finally
    Book.Free;
  end;
end;

Obiectul de opțiuni este deținut de apelant. HotXLS nu îl va elibera, motiv pentru care try..finally-ul interior este acolo și nu este opțional. Cele două proprietăți care schimbă rezultatul, nu doar îl etichetează, merită o privire mai atentă. Setarea IncludeCharts := False face mai mult decât să ascundă diagramele: elimină din pachet subdocumentele de diagramă și intrările lor din manifest, ceea ce este exact ce doriți atunci când consumatorul este un pipeline de date care s-ar poticni de ele. Generator suprascrie șirul ODF meta:generator, care altfel citește HotXLS/<version>; suprascrieți-l atunci când instrumentele din aval identifică producătorii de fișiere pentru a direcționa suportul. Dacă nimic din toate acestea nu se aplică, omiteți complet obiectul de opțiuni. Apelarea SaveAs(FileName, xlsxOpenDocumentSpreadsheet) este identică cu SaveAsODS cu valorile implicite, iar supraîncărcările de flux de pe ambele vă permit să scrieți pachetul direct într-un răspuns HTTP, fără fișier temporar

Ce citește calea de import - și ce omite în mod deliberat

Citiți cu atenție această parte înainte de a promite cuiva fidelitate de tip dus-întors. Importul ODS în HotXLS este în mod deliberat o cale ușoară. Păstrează valorile scalare ale celulelor și rezultatul din cache pe care fiecare formulă îl purta la momentul salvării și extinde în grilă rândurile și coloanele repetate. Nu aduce stiluri, expresii de formule ODS sau desene

Alegerea privind formulele este cea mai probabilă să muște, și a fost făcută intenționat. O celulă ODF stochează două lucruri unul lângă altul: expresia formulei, scrisă în dialectul OpenFormula definit în ODF 1.3 Partea 4, și ultima valoare pe care aplicația producătoare a calculat-o pentru ea. Traducerea OpenFormula în sintaxa formulelor Excel este propria sa problemă de conversie de dialect, cu cazuri limită reale legate de vocabularul funcțiilor, sintaxa referințelor și modelele de erori. Citirea valorii din cache în schimb ocolește întreaga acea clasă de traducere greșită silențioasă, astfel încât numerele pe care le importați sunt exact numerele pe care expeditorul le-a văzut ultima dată. Costul este că sosesc ca numere, nu ca formulele active care le-au produs

Modul de eșec în jurul căruia trebuie proiectat rezultă direct: o foaie de calcul ale cărei totaluri erau corecte când LibreOffice a salvat-o ultima dată se importă cu numere corecte, dar acele numere sunt acum constante. Editați o celulă de intrare, recalculați, și nimic nu se mișcă - formula a dispărut, rămâne doar rezultatul ei final. Dacă fluxul de lucru are nevoie de formule active după import, restabiliți-le programatic din propriile reguli de business prin Cell.Formula, care pe fațada XLSX preia expresia fără semnul egal de la început

Proiectarea în jurul călătoriei dus-întors asimetrice

Exportul randează din modelul complet al registrului de lucru din memorie: valori, stiluri și, dacă le cereți, diagrame și imagini. Importul returnează doar valori. Așadar, etapa de la .xlsx la .ods are fidelitate ridicată, iar etapa de la .ods la .xlsx aduce înapoi valori și rezultate din cache, dar fără stilizare și fără formule active. Înlănțuiți cele două și asimetria se acumulează. Un ciclu complet .xlsx către .ods către .xlsx scrie totul fidel la ieșire și pierde stilurile și formulele la reintrare, chiar dacă nimic nu a mers greșit la niciunul dintre pași

Diagramă a tur-returului ODS asimetric HotXLS din Delphi: export cu fidelitate completă din modelul de registru de lucru din memorie și un import doar-valori care lasă formulele ca constante
Exportul randează modelul complet din memorie, în timp ce importul returnează valori și rezultate în cache, astfel încât un ciclu complet .xlsx către .ods către .xlsx pierde discret stiluri și formule vii
Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // formatul este detectat automat
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // Valorile și rezultatele din cache ale formulelor sunt prezente
    // după un import ODS; stilurile și formulele active nu sunt.
    // Reconstruiți ce depinde de acestea în pipeline-ul din aval, înainte de salvare.
    Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
    Book.SaveAs('vendor-revision.xlsx');
  end;
finally
  Book.Free;
end;

Modelul arhitectural care rezultă din toate acestea: tratați fișierele .ods primite ca fluxuri de date, nu ca documente de editat pe loc. Păstrați registrul de lucru canonic în .xlsx, citiți valori din reviziile clienților și emiteți ODS proaspăt la cerere din copia canonică. Verificarea aparține ambelor tabere - deschideți fișierele exportate în LibreOffice Calc, consumatorul ODF de referință, și în Excel, care citește ODS de ani de zile, dar nu este de acord cu LibreOffice la marginile suportului pentru diagrame și stiluri. Numărul de foi, câteva celule cheie și prezența diagramelor formează o verificare fumigenă suficientă pentru fiecare profil de export

Triajul unui fișier ODS înainte de a vă angaja la un import

Când un punct final acceptă încărcări, listarea numelor foilor este mult mai ieftină decât o analiză completă și prinde din timp surprizele structurale:

Diagramă a porții de triaj la încărcare HotXLS în Delphi, în care GetODSSheetNames respinge pachetele ODS necitibile și foile lipsă înainte de a rula un import complet
O sondă GetODSSheetNames costă cu mult mai puțin decât o parsare completă și prinde eșecul foii redenumite cât timp eroarea poate încă numi fișierul
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;

Convenția de returnare încurcă oamenii: apelurile HotXLS returnează în general un număr pozitiv sau 1 la succes și -1 la eșec, golind lista pe măsură ce eșuează, așa că testați <= 0 în loc să comparați cu o singură valoare pozitivă specifică. GetODSSheetNames nici nu resetează, nici nu populează instanța registrului de lucru, astfel încât un singur obiect sondă poate verifica un întreg director de fișiere primite. Verificările structurale de acest tip prind cea mai comună eroare din lumea reală - un analist care redenumește sau șterge o foaie înainte de a retrimite revizia - chiar la poartă, unde mesajul de eroare poate încă numi fișierul și foaia lipsă, în loc să apară ca o referință nilă trei niveluri mai jos

Dacă construiți un pipeline de conversie mai larg în jurul acestui lucru, tiparul de bancă de lucru pentru audit și conversie a registrelor de lucru arată cum să inventariați caracteristicile unui fișier înainte de a alege un format țintă, iar ghidul de performanță pentru registre de lucru mari menține exporturile în loturi în limite rezonabile de memorie

HotXLS este o bibliotecă nativă Delphi și C++Builder pentru foi de calcul, cu cod sursă complet; lista completă de funcționalități și detaliile de licențiere se află pe pagina de produs HotXLS Delphi Component