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
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
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:
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