HotXLS, izvorna Delphi i C++Builder Excel knjižnica, izgrađena je za XLSX povratno putovanje (round-trip) bez gubitaka: otvorite radnu knjigu, promijenite jednu ćeliju, spremite, i kupčeva prilagođena tema, strani extLst prošireni blokovi i lanac izračuna (calculation chain) preživljavaju. Tri mehanizma to čine mogućim — doslovno predmemoriranje (verbatim caching) datoteke xl/theme/theme1.xml, ponovna serijalizacija nepoznatih blokova <ext> temeljena na događajima te svježa i prema specifikaciji valjana datoteka xl/calcChain.xml pri svakom spremanju radne knjige s formulama
Scenarij koji motivira sva tri mehanizma deprimirajuće je čest. Usluga naplate učitava predložak koji je kupac dizajnirao u Excelu — korporativnu temu boja, mini-grafikone (sparklines) u KPI stupcu, pravilo uvjetnog oblikovanja dodano novijom verzijom Excela — upisuje jedan ukupni iznos računa u ćeliju B3 i sprema. Kupac otvara rezultat, a korporativne boje su se vratile na tvornički plavu Office boju, mini-grafikoni su nestali, a Excel nudi "popravak" datoteke. Ništa u kodu nije dotaknulo nijednu od tih značajki. Knjižnica jest, jednostavno spremanjem
Zašto Excel datoteke gube oblikovanje nakon uređivanja pomoću knjižnice?
Excel datoteke gube oblikovanje nakon uređivanja pomoću knjižnice jer većina knjižnica ne uređuje datoteku — one je ponovno grade. Paket .xlsx je ZIP XML dijelova: xl/workbook.xml, jedna xl/worksheets/sheetN.xml po listu, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml i drugi. Tipična knjižnica analizira te dijelove u model objekta pri otvaranju i regenerira svaki dio iz tog modela pri spremanju. Bilo koja značajka koju model ne predstavlja — tema koju nikada nije analizirao, blok proširenja iz novijeg Excela — nema gdje živjeti u memoriji, pa je regenerirani dio tiho izostavlja
Specifikacija ECMA-376 predvidjela je polovicu ovog problema. SpreadsheetML definira extLst (ECMA-376 Dio 1, "Područje za pohranu podataka budućih značajki", §18.2.10 za element na razini radne knjige) kao označenu točku proširenja: noviji proizvođači tamo smještaju značajke, svaku omotanu u element <ext> koji nosi atribut uri koji identificira značajku, a od starijih se potrošača očekuje da sačuvaju ono što ne razumiju. Mini-grafikoni, rezači (slicers) i novije vrste uvjetnog oblikovanja putuju na ovaj način. Knjižnica koja odbacuje nepoznate blokove <ext> stoga nije samo manjkava (lossy) — ona krši ugovor o kompatibilnosti prema naprijed oko kojeg je format dizajniran. Pitanje koje treba postaviti svakoj knjižnici proračunskih tablica koju procjenjujete je izravno: ako promijenim jednu ćeliju, što se još mijenja
Kako HotXLS čuva prilagođenu temu bajt po bajt?
HotXLS čuva temu radne knjige predmemoriranjem izvornih bajtova xl/theme/theme1.xml pri otvaranju i njihovim doslovnim zapisivanjem natrag pri spremanju. Dio s temom (ECMA-376 Dio 1, §14.2.7) je DrawingML, a ne SpreadsheetML — sheme boja, sheme fontova, sheme formata — i mehanizam proračunskih tablica nema razloga duboko ga modelirati. Ranije verzije HotXLS-a regenerirale su fiksnu Office temu pri svakom spremanju, što je točno gore opisani neuspjeh s povratkom na tvorničke boje; od verzije v2.89.46 tema otvorenog paketa pohranjuje se sirova i ponovno emitira netaknuta, a ugrađena Office tema generira se samo za radne knjige stvorene ispočetka. Sirovi bajtovi su najjače moguće jamstvo vjernosti: nema analiziranja, nema ponovne serijalizacije, nema šanse za odstupanje
Doslovna kopija namjerno pobjeđuje programski pristup temi. Klasa TXLSXWorkbook izlaže ThemeMajorFont and ThemeMinorFont kako biste mogli odabrati heading i body fontove za nove radne knjige, no kada se pri otvaranju uhvati doslovna tema, ti postavljači (setters) nemaju utjecaja na spremljenu datoteku — povratno putovanje ima prioritet. Ako uistinu trebate promijeniti temu postojeće radne knjige, to je znak da predložak uredite u samom Excelu, a ne putem API-ja usmjerenog na podatke. Svakodnevni slučaj uopće ne treba 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;
Što se događa s nepoznatim extLst blokovima pri spremanju?
HotXLS zahvaća svaki prošireni blok <ext> na razini lista koji izvorno ne modelira i ponovno ga reproducira u extLst spremljenog lista, tako da značajke zapisane novijim verzijama Excela preživljavaju povratno putovanje netaknute. Od verzije v2.131.0, uhvaćeni fragmenti su vidljivi kroz svojstvo samo za čitanje RawWorksheetExts, što je TStringList na svakom XLSX listu, što čini jamstvo provjerljivim iz testnog koda umjesto činom vjere:
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;
Detalj implementacije koji vrijedi znati jeste da je zahvaćanje ponovna serijalizacija na razini događaja, a ne sirova kopija bajtova. HotXLS-ov čitač XML-a u toku ne izlaže izvorne pomake, pa se nepoznato podstablo ponovno gradi od događaja Element, Text i EndElement dok prolaze kroz tok. Taj pristup skriva jednu klasičnu zamku: samozatvarajući element poput <a/> pokreće samo događaj Element označen kao prazan, a nikada EndElement, pa bilo koji brojač dubine koji se smanjuje isključivo pri EndElementu nikada neće vidjeti zatvaranje podstabla. Riješite to, i obnovljeni fragment je semantički ekvivalentan izvorniku — navođenje atributa i samozatvarajući oblici se normaliziraju, tako da nije bajt-identičan, ali Excel čita značenje, a ne bajtove. Dva svojstva Excelova vlastita izlaza čine reprodukciju sigurnom: Excel deklarira potrebne atribute xmlns na elementu <ext> ili unutar njega, pa je svaki uhvaćeni fragment prostorno-nazivno (namespace) samostalan. Ta je samostalnost razlog zašto dupliciranje lista unutar ili preko radnih knjiga može prenijeti strane blokove uz obično dodjeljivanje string-liste
Zapisivanje calcChain.xml kako bi Excel vjerovao vašim formulama
HotXLS zapisuje xl/calcChain.xml (dio lanca izračuna, ECMA-376 Dio 1, §12.3.1) kad god spremljena radna knjiga sadrži formule, i bira između dva redoslijeda. Ako je grafikon ovisnosti formula već izgrađen i trenutan — pozvali ste Recalculate nakon posljednjeg uređivanja — lanac se emitira u potpunom topološkom redoslijedu, ovisnosti prije ovisnih, s dodanim članovima kružne reference na kraju. U suprotnom, ćelije se popisuju redoslijedom u dokumentu. Oba su načina točna: Microsoftove bilješke o implementaciji formata, [MS-XLSX], tretiraju lanac izračuna kao nagovještaj koji Excel provjerava i preuređuje tijekom učitavanja, pa je svaki potpuni popis legalan, a HotXLS namjerno odbija forsirati izgradnju grafikona unutar SaveAs — stvaranje bridova je kvadratno u odnosu na broj ćelija, što je neprihvatljiv skriveni trošak pri spremanju radne knjige s milijun ćelija
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');
Zašto brinuti o dijelu koji Excel tretira kao savjetodavan? Zato što je njegova odsutnost signal. Neki potrošači — heuristika popravka, preglednici trećih strana, alati za usporedbu (diff) — očekuju da radna knjiga s formulama nosi lanac izračuna, a knjižnica koja tiho odbacuje taj dio pri spremanju proizvodi datoteke koje su suptilno različite od svega što Excel piše. Emitiranje valjanog lanca drži izlaz unutar okvira onoga za što je ostatak ekosustava testiran, što je tiha, neglamurozna jezgra inženjeringa povratnog putovanja
Gdje prestaje povratno putovanje bez gubitaka
Poštenje je ovdje važnije od marketinškog okvira, pa granice zaslužuju jednaku pozornost. HotXLS ne kopira cijeli paket bajt po bajt: XML radnog lista, stilovi, dijeljeni nizovi znakova (shared strings) i dijelovi radne knjige regeneriraju se iz analiziranog modela, tako da je izlaz semantički vjeran, ali ne i binarno identičan — sami lokalni ZIP zaglavlja nose svježe DOS vremenske oznake. Uhvaćeni fragmenti <ext> vraćaju se normalizirani, kao što je gore opisano. Programska nadjačavanja fontova teme se zanemaruju kada je prisutna doslovna tema. Mreža očuvanja ima definiranu strukturu: značajke koje HotXLS izvorno modelira (mini-grafikoni, na primjer, analiziraju se i ponovno zapisuju umjesto da se slijepo kopiraju) plus strani sadržaj extLst plus doslovno predmemorirani dijelovi. Dio koji nije modeliran niti se nalazi unutar točke proširenja — recimo, prilagođeni dio nekog egzotičnog dodatka — izvan je tri mehanizma koje ovaj članak pokriva, stoga testirajte svoje stvarne predloške umjesto pretpostavljanja
Srodni radovi na očuvanju nadopunjuju sliku. Projekti VBA i reference na vanjske radne knjige prolaze kroz spremanje prema istoj filozofiji zadržavanja onoga što se ne modelira, što je pokriveno u pratećem članku o očuvanju VBA i vanjskih poveznica, a svojstva dokumenta u docProps imaju vlastiti read-write API umjesto da budu tiho odbačena. Kada procjenjujete bilo koju knjižnicu proračunskih tablica, pokrenite test s jednom ćelijom: otvorite značajkama bogatu produkcijsku radnu knjigu, promijenite jednu vrijednost, spremite i usporedite otpakirane dijelove s izvornikom. Ono što se promijenilo izvan lista koji ste dotaknuli govori vam više o knjižnici nego bilo koja tablica značajki
Mehanizmi povratnog putovanja koji su ovdje opisani — doslovno zadržavanje teme od verzije v2.89.46, zahvaćanje stranog extLst i emitiranje calcChain.xml od verzije v2.131.0 — dolaze u trenutnoj komponenti HotXLS Delphi Excel Component, čija stranica proizvoda dokumentira cjelokupni skup značajki za čitanje i pisanje XLSX datoteka za Delphi i C++Builder