Odborný článok

Bezstratový prenos XLSX (Round-Trip) v Delphi: Téma, extLst, calcChain

Knižnica HotXLS, natívna Excel knižnica pre Delphi a C++Builder, je navrhnutá pre bezstratový prenos XLSX (round-trip): otvoriť zošit, zmeniť jednu bunku, uložiť a vlastná téma zákazníka, cudzie rozširujúce bloky extLst aj kalkulačný reťazec (calculation chain) prežijú. Tri mechanizmy to umožňujú — doslovné ukladanie xl/theme/theme1.xml do vyrovnávacej pamäte, opätovná serializácia neznámych blokov <ext> na základe udalostí a vytvorenie nového, podľa špecifikácie platného súboru xl/calcChain.xml pri každom uložení zošita so vzorcami

Scenár, ktorý motivuje všetky tri mechanizmy, je až frustrujúco bežný. Fakturačná služba načíta šablónu, ktorú zákazník navrhol v Exceli — firemnú farebnú tému, krivky (sparklines) v KPI stĺpci, pravidlo podmieneného formátovania pridané novšou verziou Excelu — zapíše celkovú sumu faktúry do bunky B3 a uloží ju. Zákazník otvorí výsledok a farby značky sa vrátia na predvolenú modrú farbu Office, krivky zmiznú a Excel ponúkne možnosť „opraviť“ súbor. Nič v kóde sa týchto funkcií nedotklo. Urobila to knižnica, jednoducho tým, že súbor uložila

Prečo Excel súbory po úprave knižnicou strácajú formátovanie?

Excel súbory strácajú formátovanie po úprave knižnicou, pretože väčšina knižníc súbor neupravuje — rekonštruujú ho. Balík .xlsx je archív ZIP obsahujúci časti XML: xl/workbook.xml, jeden súbor xl/worksheets/sheetN.xml na hárok, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml a ďalšie. Typická knižnica tieto časti pri otvorení analyzuje do objektového modelu a pri uložení z tohto modelu vygeneruje každú časť znova. Akákoľvek funkcia, ktorou model nereprezentuje — téma, ktorú nikdy neanalyzoval, alebo rozširujúci blok z novšieho Excelu — nemá v pamäti kde existovať, takže pregenerovaná časť ju potichu vynechá

Štandard ECMA-376 predvídal polovicu tohto problému. Súčasť SpreadsheetML definuje element extLst (ECMA-376 Časť 1, „Future Feature Data Storage Area“, §18.2.10 pre úroveň zošita) ako určený bod rozšírenia: novšie aplikácie tam ukladajú funkcie, z ktorých každá je zabalená v elemente <ext> nesúcom atribút uri, ktorý identifikuje danú funkciu, pričom od starších čítačiek sa očakáva, že zachovajú to, čomu nerozumejú. Krivky, rýchle filtre (slicers) a novšie typy podmieneného formátovania sa prenášajú týmto spôsobom. Knižnica, ktorá zahadzuje neznáme bloky <ext>, nie je len stratová — porušuje dohodu o spätnej kompatibilite, na ktorej bol formát navrhnutý. Otázka na akúkoľvek knižnicu pre tabuľkové procesory, ktorou hodnotíte, je priama: ak zmením jednu bunku, čo všetko sa zmení?

Ako HotXLS zachováva vlastnú tému bajt po bajte?

HotXLS zachováva tému zošita tak, že pri otvorení uloží pôvodné bajty súboru xl/theme/theme1.xml do vyrovnávacej pamäte a pri uložení ich zapíše doslovne späť. Časť témy (ECMA-376 Časť 1, §14.2.7) je DrawingML a nie SpreadsheetML — farebné schémy, schémy písiem, schémy formátov — a tabuľkový procesor nemá dôvod ju podrobne modelovať. Staršie verzie HotXLS regenerovali pri každom uložení fixnú tému Office, čo vledlo presne k zlyhaniu „navrátenia farieb značky“ popísanému vyššie; od verzie v2.89.46 sa téma otvoreného balíka ukladá v surovom stave a znova vygeneruje nedotknutá, pričom vstavaná téma Office sa generuje len pre zošity vytvorené od nuly. Surové bajty sú najsilnejšou možnou zárukou vernosti: žiadna analýza, žiadna opätovná serializácia, žiadna šanca na odchýlku

Doslovná kópia má zámerne prednosť pred programovým prístupom k téme. Trieda TXLSXWorkbook sprístupňuje vlastnosti ThemeMajorFont a ThemeMinorFont, takže môžete vybrať písma pre nadpisy a telo pre nové zošity, ale ak bola pri otvorení zachytená téma, tieto nastavenia nemajú na uložený súbor žiadny vplyv — prednosť má vernosť prenosu. Ak naozaj potrebujete zmeniť tému existujúceho zošita, je to signál pre úpravu šablóny priamo v Exceli a nie cez dátovo orientované API. Každodenný prípad nevyžaduje vôbec žiadne API:

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('branded-invoice.xlsx');
    Book.Sheets[0].Cells[3, 2].Value := 42750.00;  // the one edit
    Book.SaveAs('branded-invoice-out.xlsx');
    // theme1.xml in the output is byte-identical to the input
  finally
    Book.Free;
  end;
end;

Čo sa stane s neznámymi blokmi extLst pri uložení?

HotXLS zachytáva každý blok <ext> na úrovni hárka, ktorý natívne nemodeluje, a prehrá ho do extLst uloženého hárka, takže funkcie zapísané novšími verziami Excelu prežijú prenos nedotknuté. Od verzie v2.131.0 sú zachytené fragmenty viditeľné cez vlastnosť RawWorksheetExts určenú len na čítanie, čo je TStringList na každom XLSX hárku, vďaka čomu je táto záruka overiteľná z testovacieho kódu a nie len predmetom viery:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  i: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('from-newer-excel.xlsx');
    Sheet := Book.Sheets[0];
    WriteLn(Format('%d foreign ext block(s) captured',
      [Sheet.RawWorksheetExts.Count]));
    for i := 0 to Sheet.RawWorksheetExts.Count - 1 do
      WriteLn(Copy(Sheet.RawWorksheetExts[i], 1, 100)); // peek at each uri
  finally
    Book.Free;
  end;
end;

Detail implementácie, ktorý stojí za to poznať, je, že zachytenie je opätovná serializácia na úrovni udalostí a nie kópia surových bajtov. Streaming XML čítač v HotXLS neposkytuje žiadne zdrojové posuny, takže neznámy podstrom sa rekonštruuje z udalostí Element, Text a EndElement, ako prechádzajú tokom. Tento prístup skrýva jednu klasickú pascu: samozatvárací element, napríklad <a/>, vyvolá iba udalosť Element s príznakom empty a nikdy nie EndElement, so any depth counter that decrements solely on EndElement will never see the subtree close. Ak to spracujete, prestavaný fragment je sémanticky ekvivalentný originálu — citovanie atribútov a samozatváracie formy sú normalizované, takže nie je bajtovo identický, ale Excel číta význam, nie bajty. Dve vlastnosti vlastného výstupu Excelu robia prenos bezpečným: Excel deklaruje potrebné atribúty xmlns na elemente <ext> alebo vo vnútri neho, so each captured fragment is namespace-self-contained, a táto samostatnosť je tiež dôvodom, prečo duplikovanie hárka vo vnútri alebo naprieč zošitmi môže preniesť cudzie bloky pomocou jednoduchého priradenia string-listu

Zápis calcChain.xml, aby Excel dôveroval vašim vzorcom

HotXLS zapisuje xl/calcChain.xml (časť Calculation Chain, ECMA-376 Časť 1, §12.3.1) zakaždým, keď uložený zošit obsahuje vzorce, pričom si vyberá medzi dvoma usporiadaniami. Ak bol graf závislostí vzorcov už vytvorený a je aktuálny — zavolali ste Recalculate po poslednej úprave — reťazec sa vygeneruje v plnom topologickom poradí, závislosti pred nasledovníkmi, s prípadnými členmi kruhového odkazu pripojenými na konci. V opačnom prípade sú bunky uvedené v poradí dokumentu. Obe možnosti sú správne: poznámky k implementácii formátu od spoločnosti Microsoft, [MS-XLSX], pristupujú k tomuto reťazcu ako k pomôcke, ktorú Excel pri načítaní overuje a reorganizuje, takže akýkoľvek kompletný zoznam je legálny a HotXLS zámerne nevynucuje zostavenie grafu vo vnútri metódy SaveAs — vytváranie hrán je kvadratické k počtu buniek, čo by bol neprijateľný skrytý náklad pri ukladaní zošita s miliónom buniek

Book.Open('model.xlsx');
Book.Sheets[0].Cells[10, 4].Formula := '=SUM(D2:D9)';
// Saved now, calcChain.xml lists formula cells in document order.
// After Recalculate the dependency graph exists, so the same save
// emits a full topological order instead:
Book.Recalculate;
Book.SaveAs('model-out.xlsx');

Kde bezstratový prenos končí

Úprimnosť tu zaváži viac než marketingové zaškrtávacie políčko, preto si limity zaslúžia rovnakú pozornosť. HotXLS nekopíruje celý balík bajt po bajte: XML hárkov, štýly, zdieľané reťazce a časti zošita sa generujú z analyzovaného modelu, takže výstup je sémanticky verný, ale nie binárne identický — uzly lokálnych hlavičiek ZIP nesú nové DOS časové pečiatky. Zachytené fragmenty <ext> sa vracajú normalizované, ako je popísané vyššie. Programové prepisy písiem témy sa ignorujú, ak je prítomná doslovná téma. A záchranná sieť má definovanú veľkosť oka: obsahuje funkcie, ktoré HotXLS modeluje natívne (napríklad krivky sú analyzované a prepisované, nie slepo kopírované), plus cudzí obsah extLst, plus časti uložené doslovne vo vyrovnávacej pamäti. Časť, ktorá nie je modelovaná ani sa nenachádza v bode rozšírenia — napríklad vlastná časť nejakého exotického doplnku — spadá mimo troch mechanizmov, o ktorých hovorí tento článok, preto testujte svoje reálne šablóny a nepredpokladajte

Súvisiace konzervačné práce dopĺňajú obraz. Projekty VBA a odkazy na externé zošity prechádzajú uložením s rovnakou filozofiou zachovania-toho-čo-nemodelujeme, popísanou v sprievodnom článku o zachovaní projektov VBA a externých odkazov, pričom vlastnosti dokumentu v docProps majú svoje vlastné API pre čítanie a zápis a nie sú potichu zahodené. Keď hodnotíte akúkoľvek knižnicu pre tabuľkové procesory, spustite test s jednou bunkou: otvorte produkčný zošit plný funkcií, zmeňte jednu hodnotu, uložte ho a porovnajte rozbalené časti s originálom. To, čo sa zmenilo mimo hárka, ktorého ste sa dotkli, vám o knižnici povie viac než akákoľvek tabuľka vlastností

Mechanizmy prenosu (round-trip) popísané v tomto článku — doslovné zachovanie témy od verzie v2.89.46, zachytenie cudzích extLst a generovanie calcChain.xml od verzie v2.131.0 — sa dodávajú v aktuálnom komponente HotXLS Delphi Excel Component, ktorého stránka produktu dokumentuje kompletnú sadu funkcií pre čítanie a zápis formátu XLSX pre Delphi a C++Builder

Domov · Hľadať · losLab.com