Teknisk artikkel

Tapsfri XLSX-rundtur i Delphi: tema, extLst, calcChain

HotXLS, det native Excel-biblioteket for Delphi og C++Builder, er bygget for tapsfrie XLSX-rundturer: åpne en arbeidsbok, endre én celle, lagre, og kundens egendefinerte tema, fremmede extLst-utvidelsesblokker og beregningskjeden overlever alle sammen. Tre mekanismer får det til å virke — ordrett hurtiglagring av xl/theme/theme1.xml, hendelsesbasert reserialisering av ukjente <ext>-blokker, og en fersk, spesifikasjonsgyldig xl/calcChain.xml ved hver lagring av en formelarbeidsbok

Tre mekanismer bak en tapsfri HotXLS XLSX-rundtur i Delphi: xl/theme/theme1.xml hurtiglagret som rå byte og skrevet tilbake byteidentisk, fremmede extLst-blokker fanget fra XML-hendelser og spilt av på nytt, og en fersk spesifikasjonsgyldig calcChain.xml skrevet ved hver formellagring
Hver bevart del følger sin egen vei gjennom lagringen — ordrette temabyte, extLst-avspilling på hendelsesnivå og en regenerert beregningskjede — mens regneark-XML og stiler bygges opp på nytt fra modellen

Situasjonen som motiverer alle tre, er nedslående vanlig. En faktureringstjeneste laster inn en mal kunden utformet i Excel — bedriftens fargetema, sparklines i en KPI-kolonne, en regel for betinget formatering lagt til av en nyere Excel-utgave — skriver én fakturasum inn i celle B3, og lagrer. Kunden åpner resultatet, og merkevarefargene har snappet tilbake til Office-standardens blåfarge, sparklines er borte, og Excel tilbyr seg å «reparere» filen. Ingenting i koden rørte noen av de funksjonene. Det gjorde biblioteket, ganske enkelt ved å lagre

Hvorfor mister Excel-filer formatering etter bibliotekredigeringer?

Excel-filer mister formatering etter bibliotekredigeringer fordi de fleste bibliotekene ikke redigerer filen — de bygger den opp på nytt. En .xlsx-pakke er en ZIP av XML-deler: xl/workbook.xml, én xl/worksheets/sheetN.xml per ark, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml, og flere. Et typisk bibliotek parser disse delene inn i en objektmodell ved åpning og regenererer hver del fra den modellen ved lagring. Enhver funksjon modellen ikke representerer — et tema den aldri parset, en utvidelsesblokk fra et nyere Excel — har ingen steder å bo i minnet, så den regenererte delen utelater den stille

ECMA-376 forutså halvparten av dette problemet. SpreadsheetML definerer extLst (ECMA-376 del 1, «Future Feature Data Storage Area», §18.2.10 for elementet på arbeidsboknivå) som et utpekt utvidelsespunkt: nyere produsenter parkerer funksjoner der, hver pakket inn i et <ext>-element med et uri-attributt som identifiserer funksjonen, og eldre konsumenter forventes å bevare det de ikke forstår. Sparklines, slicere og nyere typer betinget formatering reiser alle på denne måten. Et bibliotek som kaster ukjente <ext>-blokker, er derfor ikke bare tapsbringende — det bryter forovervendt-kompatibilitetskontrakten formatet ble utformet rundt. Spørsmålet du skal stille til ethvert regnearkbibliotek du vurderer, er sylskarpt: hvis jeg endrer én celle, hva annet endrer seg

Hvordan beholder HotXLS et egendefinert tema byte for byte?

HotXLS bevarer en arbeidsboks tema ved å hurtiglagre originalbytene i xl/theme/theme1.xml ved åpning og skrive dem ordrett tilbake ved lagring. Temadelen (ECMA-376 del 1, §14.2.7) er DrawingML, ikke SpreadsheetML — fargeskjemaer, fontskjemaer, formatskjemaer — og en regnearkmotor har ingen grunn til å modellere den i dybden. Tidligere HotXLS-versjoner regenererte et fast Office-tema ved hver lagring, som er nøyaktig feilen med «merkevarefarger som snapper tilbake» ovenfor; siden v2.89.46 lagres den åpnede pakkens tema rått og skrives ut igjen urørt, og det innebygde Office-temaet genereres bare for arbeidsbøker opprettet fra bunnen av. Rå byte er den sterkeste troskapsgarantien som finnes: ingen parsing, ingen reserialisering, ingen sjanse for avdrift

Den ordrette kopien vinner bevisst over programmatisk temategang. TXLSXWorkbook eksponerer ThemeMajorFont og ThemeMinorFont slik at du kan velge skrifttyper for overskrifter og brødtekst i nye arbeidsbøker, men når et ordrett tema ble fanget ved åpning, har de setterne ingen virkning på den lagrede filen — rundturen har forrang. Trenger du virkelig å endre temaet i en eksisterende arbeidsbok, er det et signal om å redigere malen i Excel selv i stedet for gjennom et dataorientert API. Hverdagstilfellet trenger ikke noe API i det hele tatt:

HotXLS hurtiglagrer de rå bytene i xl/theme/theme1.xml ved åpning og skriver dem byteidentisk tilbake ved lagring, mens et bibliotek som bygger opp fra modellen regenererer et standard Office-tema og snapper kundens merkevarefarger tilbake
Å hurtiglagre theme1.xml ordrett trenger ingen temamodell i det hele tatt, og ThemeMajorFont sammen med ThemeMinorFont styler bare arbeidsbøker som ikke bærer et fanget tema
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('branded-invoice.xlsx');
    Book.Sheets[0].Cells[3, 2].Value := 42750.00;  // den ene redigeringen
    Book.SaveAs('branded-invoice-out.xlsx');
    // theme1.xml i utdataene er byteidentisk med inndataene
  finally
    Book.Free;
  end;
end;

Hva skjer med ukjente extLst-blokker ved lagring?

HotXLS fanger hver <ext>-blokk på regnearknivå som den ikke modellerer nativt, og spiller den av igjen inn i det lagrede regnearkets extLst, slik at funksjoner skrevet av nyere Excel-utgaver overlever rundturen intakte. Siden v2.131.0 er de fangede fragmentene synlige gjennom den skrivebeskyttede RawWorksheetExts-egenskapen, en TStringList på hvert XLSX-regneark, noe som gjør garantien reviderbar fra testkode i stedet for en trosakt:

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)); // kikk på hver uri
  finally
    Book.Free;
  end;
end;

Implementasjonsdetaljen som er verdt å kjenne til, er at fangsten er en reserialisering på hendelsesnivå, ikke en rå bytekopi. HotXLS' strømmende XML-leser eksponerer ingen kildeoffseter, så det ukjente deltreet bygges opp på nytt fra Element-, Text- og EndElement-hendelser etter hvert som de strømmer forbi. Den tilnærmingen skjuler én klassisk felle: et selvlukkende element som <a/> utløser bare en Element-hendelse merket tom og aldri en EndElement, så enhver dybdeteller som bare dekrementerer på EndElement, vil aldri se deltreet lukke seg. Håndter det, og det gjenoppbygde fragmentet er semantisk ekvivalent med originalen — attributtsitering og selvlukkende former normaliseres, så det er ikke byteidentisk, men Excel leser mening, ikke byte. To egenskaper ved Excels egne utdata gjør avspillingen trygg: Excel erklærer de nødvendige xmlns-attributtene på <ext>-elementet eller inne i det, så hvert fanget fragment er navneromsselvstendig, og nettopp den selvstendigheten er grunnen til at duplisering av et regneark innenfor eller på tvers av arbeidsbøker kan ta de fremmede blokkene med seg gjennom en vanlig strenglistetilordning

Å skrive calcChain.xml slik at Excel stoler på formlene dine

HotXLS skriver xl/calcChain.xml (beregningskjededelen, ECMA-376 del 1, §12.3.1) hver gang den lagrede arbeidsboken inneholder formler, og den velger mellom to rekkefølger. Hvis formelavhengighetsgrafen allerede er bygget og er aktuell — du kalte Recalculate etter din siste redigering — skrives kjeden ut i full topologisk rekkefølge, avhengigheter før avhengige, med eventuelle medlemmer av sirkulære referanser føyd til på slutten. Ellers listes cellene i dokumentrekkefølge. Begge er korrekte: Microsofts implementasjonsnotater for formatet, [MS-XLSX], behandler beregningskjeden som et hint Excel verifiserer og omordner under innlasting, så enhver fullstendig oppramsing er lovlig, og HotXLS nekter bevisst å tvinge frem en grafbygging inne i SaveAs — kantkonstruksjon er kvadratisk i antall celler, en uakseptabel skjult kostnad ved lagring av en million celler

HotXLS skriver xl/calcChain.xml i topologisk rekkefølge når Recalculate har bygget avhengighetsgrafen, og i dokumentrekkefølge ellers; Excel behandler begge fullstendige oppramsingene som et hint og omordner dem under innlasting
Begge rekkefølgene forblir lovlige fordi Excel verifiserer kjeden på nytt ved innlasting, og HotXLS tvinger aldri frem grafbyggingen med kvadratisk kostnad inne i SaveAs
Book.Open('model.xlsx');
Book.Sheets[0].Cells[10, 4].Formula := '=SUM(D2:D9)';
// Lagres det nå, lister calcChain.xml formelcellene i dokumentrekkefølge.
// Etter Recalculate finnes avhengighetsgrafen, så den samme lagringen
// skriver en full topologisk rekkefølge i stedet:
Book.Recalculate;
Book.SaveAs('model-out.xlsx');

Hvorfor bry seg om en del Excel behandler som veiledende? Fordi fraværet av den er et signal. Enkelte konsumenter — reparasjonsheuristikker, tredjepartsvisere, diff-verktøy — forventer at en formelarbeidsbok bærer en beregningskjede, og et bibliotek som stille dropper delen ved lagring, produserer filer som er umerkelig ulike alt Excel skriver. Å skrive ut en gyldig kjede holder utdataene innenfor konvolutten av det resten av økosystemet er testet mot, som er den stille, uglamorøse kjernen i rundturskonstruksjon

Hvor den tapsfrie rundturen slutter

Ærlighet betyr mer enn en avkrysningsboks i markedsføringen her, så grensene fortjener like mye plass. HotXLS kopierer ikke hele pakken byte for byte: regneark-XML, stiler, delte strenger og arbeidsbokdeler regenereres fra den parsede modellen, så utdataene er semantisk trofaste, men ikke binæridentiske — ZIP-ens lokale hoder alene bærer ferske DOS-tidsstempler. Fangede <ext>-fragmenter kommer tilbake normaliserte, slik det er beskrevet ovenfor. Programmatiske overstyringer av temafonter ignoreres når et ordrett tema er til stede. Og bevaringsnettet har en definert maskevidde: funksjoner HotXLS modellerer nativt (sparklines, for eksempel, parses og skrives om i stedet for å kopieres blindt) pluss fremmed extLst-innhold pluss de ordrett hurtiglagrede delene. En del som verken er modellert eller ligger inne i et utvidelsespunkt — en eksotisk tilleggsmodul sin egendefinerte del, for eksempel — faller utenfor de tre mekanismene denne artikkelen dekker, så test de faktiske malene dine i stedet for å anta

Tilgrensende bevaringsarbeid fullfører bildet. VBA-prosjekter og eksterne arbeidsbokreferanser rir gjennom lagringen på den samme behold-det-du-ikke-modellerer-filosofien, dekket i søsterartikkelen om bevaring av VBA og eksterne lenker, og dokumentegenskaper i docProps har sitt eget les- og skrive-API i stedet for å bli stille droppet. Når du vurderer et hvilket som helst regnearkbibliotek, kjør éncelletesten: åpne en funksjonsrik produksjonsarbeidsbok, endre én enkelt verdi, lagre, og diff de utpakkede delene mot originalen. Det som endret seg utover arket du rørte, forteller deg mer om biblioteket enn noen funksjonsmatrise

Rundturmekanismene som er beskrevet her — ordrett temabevaring siden v2.89.46, fangst av fremmed extLst og utskriving av calcChain.xml siden v2.131.0 — følger med i dagens HotXLS Delphi Excel Component, hvis produktside dokumenterer hele XLSX-funksjonssettet for lesing og skriving i Delphi og C++Builder