Delphillä toteutettu raportointitaustajärjestelmä, joka on tuottanut .xlsx-tiedostoja vuosia, saa uuden vaatimuksen: julkishallinnon asiakkaan hankintasäännöt edellyttävät OpenDocument Spreadsheet -tulostetta, ja kyseisen asiakkaan analyytikot lähettävät muokkauksensa takaisin LibreOfficella tallennettuina .ods-tiedostoina. Nyt saman koodin on siis kirjoitettava ODS:ää ja luettava sitä. HotXLS, losLabin Delphille ja C++Builderille tarkoitettu natiivi Object Pascal -laskentataulukkojen kirjasto, hoitaa molemmat suunnat ilman, että Excel tai LibreOffice on asennettuna minnekään. Se ei kuitenkaan tee suunnista symmetrisiä. Vienti säilyttää paljon enemmän kuin tuonti palauttaa, ja toisin olettava tiimi näkee kaavojen ja muotoilujen haihtuvan jossakin asiakkaan muokkauksen ja seuraavan raportin välillä ilman virhettä, joka osoittaisi syyn
ODS-tuki kuuluu XLSX-julkisivuun, ei XLS-julkisivuun
HotXLS sisältää yhdessä paketissa kaksi itsenäistä luokkahierarkiaa: binäärisiä BIFF8 .xls -tiedostoja käsittelevän TXLSWorkbook-luokan yksikössä lxHandle sekä OOXML .xlsx -paketteja käsittelevän TXLSXWorkbook-luokan yksikössä lxHandleX. Jokainen OpenDocumentin aloituspiste — OpenODS, SaveAsODS, GetODSSheetNames — kuuluu TXLSXWorkbook-luokkaan. Sijoittelu ei ole mielivaltainen. OASIS ODF 1.3 -määrityksen mukainen ODS-paketti on zip-arkisto, jossa on mimetype-jäsen, manifesti ja content.xml-runko, joten se on rakenteellisesti OOXML-zipin sukulainen; BIFF8 taas on 1990-luvun binäärinen tietuevirta, jolla ei ole mitään yhteistä sen kanssa
Tällä sijoittelulla on käytännöllinen seuraus: vanhaa .xls-työkirjaa ei voi muuttaa .ods-tiedostoksi yhdellä kutsulla. Sinun on ensin siirrettävä BIFF-sisältö XLSX-malliin SaveXLSWorkbookAsXLSX-rutiinilla yksiköstä lxXlsxExport, avattava tulos uudelleen TXLSXWorkbook-luokan kautta ja vietävä se vasta sitten. Silta ei ole häviötön, ja puutteet kannattaa tuntea ennen kuin rakennat sen varaan. Se kopioi arvot, kaavat, lukumuotoilut, fontit, täytöt ja sarakeleveydet. Se pudottaa reunukset, yhdistetyt alueet, kommentit, kaaviot ja ehdollisen muotoilun. Runsaasti muotoiltu .xls-lähde saapuu ODS:ään pelkistetympänä kuin se lähti, ja tämä on sillan ominaisuus, ei ODS-kirjoittajan
Tuontipuolella tunnistus on automaattinen. Tavallinen Open-metodi tunnistaa ODS-paketin sen mimetype-jäsenestä ja palaa ylimmän tason content.xml-tarkistukseen, jos jäsentä ei ole, joten yleinen "avaa mikä tahansa käyttäjän lähettämä tiedosto" -koodipolku ei tarvitse omaa tiedostopäätteen tunnistusta. Avaamisen jälkeen SourceFormat-ominaisuus kertoo, mikä haara valittiin
Vienti ODS:ään TODSExportOptions-luokalla
Vientikutsu itsessään on yksi rivi; sitä ympäröivä asetuskohde sisältää päätökset, joista tarkastaja kysyy myöhemmin:
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // kutsuja omistaa ja vapauttaa tämän
try
Opts.Generator := 'ReportService 4.2'; // meta:generator-ylikirjoitus
Opts.IncludeCharts := True;
Opts.IncludeImages := True;
Book.SaveAsODS('quarterly-report.ods', Opts);
finally
Opts.Free;
end;
finally
Book.Free;
end;
end;
Asetuskohde on kutsujan omistuksessa. HotXLS ei vapauta sitä, minkä vuoksi sisempi try..finally on mukana eikä valinnainen. Kahta ominaisuutta, jotka muuttavat tulostetta pelkän nimeämisen sijaan, kannattaa tarkastella lähemmin. IncludeCharts := False tekee enemmän kuin piilottaa kaaviot: se poistaa kaavioiden alidokumentit ja niiden manifestimerkinnät paketista, mikä on juuri oikein, kun vastaanottaja on tietoputki, joka kompastuisi niihin. Generator korvaa ODF:n meta:generator-merkkijonon, joka olisi muuten HotXLS/<version>; korvaa se, kun jatkossa käytettävä työkalu tunnistaa tiedostotuottajat tukireititystä varten. Jos mikään tästä ei koske tapaustasi, jätä asetuskohde kokonaan pois. Kutsu SaveAs(FileName, xlsxOpenDocumentSpreadsheet) vastaa oletusasetuksilla tehtyä SaveAsODS-kutsua, ja kummankin stream-ylikuormitukset antavat kirjoittaa paketin suoraan HTTP-vastaukseen ilman väliaikaistiedostoa
Mitä tuontipolku lukee — ja mitä se tarkoituksella ohittaa
Lue tämä kohta huolellisesti ennen kuin lupaat kenellekään kierroksen säilyvän ennallaan. HotXLS:n ODS-tuonti on tarkoituksella kevyt polku. Se säilyttää skalaariarvot ja kunkin kaavan tallennushetkellä sisältämän välimuistituloksen sekä laajentaa toistetut rivit ja sarakkeet ruudukkoon. Se ei tuo tyylejä, ODS-kaavalausekkeita eikä piirroksia
Kaavoja koskeva valinta todennäköisimmin yllättää, ja se on tehty tarkoituksella. ODF-solu tallentaa rinnakkain kaksi asiaa: ODF 1.3:n osassa 4 määritellyllä OpenFormula-murteella kirjoitetun kaavalausekkeen sekä viimeisen arvon, jonka tuottava sovellus sille laski. OpenFormulan kääntäminen Excelin kaavasyntaksiin on oma murteiden muunnosongelmansa, jossa on todellisia reunatapauksia funktiosanastoissa, viittaussyntaksissa ja virhemalleissa. Välimuistiarvon lukeminen kiertää kokonaan tämän hiljaisen virhekäännöksen luokan, joten tuomasi luvut ovat täsmälleen ne luvut, jotka lähettäjä näki viimeksi. Vastineeksi ne saapuvat lukuina, eivät niitä tuottaneina elävinä kaavoina
Suunniteltava virhetilanne seuraa tästä suoraan: laskentataulukko, jonka loppusummat olivat oikein LibreOfficen viimeksi tallentaessa, tuodaan oikeine lukuineen, mutta luvut ovat nyt vakioita. Muokkaa syötesolua, laske uudelleen, eikä mikään muutu — kaava on poissa, jäljellä on vain sen lopputulos. Jos työnkulku tarvitsee tuonnin jälkeen eläviä kaavoja, luo ne uudelleen ohjelmallisesti omien liiketoimintasääntöjesi perusteella käyttämällä Cell.Formula-ominaisuutta, joka XLSX-julkisivussa vastaanottaa lausekkeen ilman alun yhtäsuuruusmerkkiä
Epäsymmetrisen kierroksen huomioiva suunnittelu
Vienti hahmontaa täydestä muistissa olevasta työkirjamallista: arvot, tyylit ja pyydettäessä myös kaaviot ja kuvat. Tuonti palauttaa vain arvot. Siksi .xlsx–.ods-vaihe on tarkka, kun taas .ods–.xlsx-vaihe tuo takaisin arvot ja välimuistitulokset mutta ei tyylejä eikä eläviä kaavoja. Kun ketjutat nämä kaksi, epäsymmetria kertautuu. Täysi .xlsx–.ods–.xlsx-kierto kirjoittaa kaiken uskollisesti ulospäin ja menettää tyylit sekä kaavat paluumatkalla, vaikka kummassakaan vaiheessa ei mennyt mitään vikaan
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // muoto tunnistettu automaattisesti
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// Arvot ja välimuistiin tallennetut kaavatulokset ovat läsnä ODS-
// tuonnin jälkeen; tyylit ja elävät kaavat eivät ole. Rakenna uudelleen mitä
// jatkoprosessi edellyttää ennen tallennusta.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
Tästä seuraava arkkitehtuurimalli on selvä: käsittele saapuvia .ods-tiedostoja tietosyötteinä, älä paikan päällä muokattavina dokumentteina. Säilytä kanoninen työkirja .xlsx-muodossa, lue arvot asiakkaan muokkauksista ja tuota tarvittaessa uusi ODS kanonisesta kopiosta. Varmennus kuuluu molempiin leireihin — avaa viedyt tiedostot LibreOffice Calcissa, ODF:n viitekuluttajassa, sekä Excelissä, joka on lukenut ODS:ää jo vuosia mutta eroaa LibreOfficesta kaavio- ja tyylituen reuna-alueilla. Laskentataulukoiden määrä, muutama avainsolu ja kaavioiden läsnäolo muodostavat riittävän savutestin kutakin vientiprofiilia varten
ODS-tiedoston arviointi ennen tuontiin sitoutumista
Kun päätepiste hyväksyy latauksia, laskentataulukoiden nimien luettelu on huomattavasti kevyempää kuin täysi jäsennys ja havaitsee rakenteelliset yllätykset varhain:
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;
Palautussopimus hämää ihmisiä: HotXLS-kutsut palauttavat yleensä positiivisen määrän tai arvon 1 onnistuessaan ja arvon -1 epäonnistuessaan sekä tyhjentävät luettelon epäonnistuessaan, joten testaa <= 0 sen sijaan, että vertaisit yhteen tiettyyn positiiviseen arvoon. GetODSSheetNames ei nollaa eikä täytä työkirjaesiintymää, joten yksi tarkistusobjekti voi arvioida kokonaisen saapuvien tiedostojen hakemiston. Tällaiset rakenteelliset tarkistukset havaitsevat tavallisimman todellisen virheen — analyytikko nimeää laskentataulukon uudelleen tai poistaa sen ennen muokkauksen palauttamista — jo portilla, jossa virheilmoitus voi vielä nimetä tiedoston ja puuttuvan laskentataulukon sen sijaan, että ongelma nousisi nil-viittauksena kolme kerrosta syvemmällä
Jos rakennat tämän ympärille laajempaa muunnosputkea, työkirjan auditointi- ja muunnostyöaseman malli näyttää, miten tiedoston ominaisuudet kartoitetaan ennen kohdemuodon valintaa, ja suurten työkirjojen suorituskykyopas pitää eräviennit kohtuullisissa muistirajoissa
HotXLS on natiivi Delphi- ja C++Builder-laskentataulukkokirjasto, jonka koko lähdekoodi toimitetaan mukana; täydellinen ominaisuusluettelo ja lisensointitiedot ovat HotXLS Delphi Componentin tuotesivulla