HotXLS, de native Delphi- en C++Builder Excel-bibliotheek, is gebouwd voor verliesvrije (lossless) XLSX-round-trips: open een werkmap, wijzig één cel, sla op, en het aangepaste thema van de klant, externe extension blocks extLst en de berekeningsketen (calculation chain) overleven allemaal. Drie mechanismen maken dat mogelijk — verbatim caching van xl/theme/theme1.xml, op events gebaseerde herserialisatie van onbekende <ext>-blokken en een frisse, specificatie-valide xl/calcChain.xml bij elke opslag van een formule-werkmap
Het scenario dat dit drijft komt helaas vaak voor. Een factureringsservice laadt een sjabloon dat de klant in Excel heeft ontworpen — met een eigen huisstijlkleurenthema, sparklines in een KPI-kolom en een regel voor voorwaardelijke opmaak toegevoegd door een nieuwere Excel-versie — schrijft één factuurtotaal in cel B3, en slaat op. De klant opent het resultaat en de merkkleuren zijn teruggesprongen naar het standaard Office-blauw, de sparklines zijn verdwenen en Excel biedt aan om het bestand te "repareren". Niets in de code heeft die functies aangeraakt. De bibliotheek deed dat wel, simpelweg door op te slaan
Waarom verliezen Excel-bestanden hun opmaak na bewerkingen door bibliotheken?
Excel-bestanden verliezen hun opmaak na bewerkingen door bibliotheken omdat de meeste bibliotheken het bestand niet bewerken — ze herbouwen het. Een .xlsx-pakket is een ZIP van XML-onderdelen: xl/workbook.xml, één xl/worksheets/sheetN.xml per tabblad, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml en meer. Een typische bibliotheek parseert die onderdelen bij het openen in een objectmodel en genereert elk onderdeel bij het opslaan opnieuw op basis van dat model. Elke functie die het model niet representeert — een thema dat het nooit heeft geparseerd, een extensieblok van een nieuwere Excel — heeft geen plek in het geheugen, waardoor het opnieuw gegenereerde onderdeel het stilletjes weglaat
ECMA-376 voorzag de helft van dit probleem. SpreadsheetML definieert extLst (ECMA-376 Part 1, the "Future Feature Data Storage Area", §18.2.10 voor het element op werkmapniveau) als een aangewezen uitbreidingspunt: nieuwere makers parkeren daar functies, elk verpakt in een element <ext> met een attribuut uri dat de functie identificeert, en er wordt verwacht dat oudere consumenten behouden wat ze niet begrijpen. Sparklines, slicers en nieuwere typen voorwaardelijke opmaak reizen allemaal op deze manier. Een bibliotheek die onbekende <ext>-blokken weggooit, is daarom niet alleen verliesgevend — het schendt het contract voor voorwaartse compatibiliteit waaromheen het formaat is ontworpen. De vraag die u aan elke spreadsheetbibliotheek moet stellen die u evalueert, is hard: als ik één cel verander, wat veranderer er dan nog meer
Hoe behoudt HotXLS een aangepast thema byte-voor-byte?
HotXLS behoudt het thema van een werkmap door de originele bytes van xl/theme/theme1.xml op te slaan bij het openen en deze bij het opslaan letterlijk terug te schrijven. Het thema-onderdeel (ECMA-376 Part 1, §14.2.7) is DrawingML, geen SpreadsheetML — kleurenschema's, lettertypeschema's, formaatschema's — en een spreadsheet-engine heeft geen reden om dit diepgaand te modelleren. Eerdere HotXLS-versies genereerden bij elke opslag een vast Office-thema, wat precies de hierboven genoemde fout oplevert; sinds v2.89.46 wordt het thema van het geopende pakket ruw opgeslagen en onaangeroerd opnieuw verzonden, en wordt het ingebouwde Office-thema alleen gegenereerd voor werkmappen die vanaf nul zijn opgebouwd. Ruwe bytes zijn de sterkst mogelijke kwaliteitsgarantie: geen parsing, geen herserialisatie, geen kans op afwijkingen
Hoe behoudt HotXLS een aangepast thema byte-voor-byte?
De letterlijke kopie wint het bewust van programmatische toegang tot het thema. TXLSXWorkbook stelt ThemeMajorFont en ThemeMinorFont beschikbaar zodat u lettertypen voor koppen en hoofdtekst kunt kiezen voor nieuwe werkmappen, maar wanneer bij het openen een letterlijk thema is vastgelegd, hebben die setters geen effect op het opgeslagen bestand — de round-trip krijgt voorrang. Als u het thema van een bestaande werkmap echt moet wijzigen, is dat een signaal om de sjabloon in Excel zelf te bewerken in plaats van via een gegevensgerichte API. De alledaagse praktijk heeft helemaal geen API nodig:
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;
Wat gebeurt er met onbekende extLst-blokken bij het opslaan?
HotXLS legt elk element <ext> op tabbladniveau vast dat het niet standaard modelert, en speelt dit af in de extLst of het opgeslagen tabblad, zodat functies die door nieuwere Excel-builds zijn geschreven de round-trip intact overleven. Sinds v2.131.0 zijn de vastgelegde fragmenten zichtbaar via de alleen-lezen eigenschap RawWorksheetExts, een TStringList op elk XLSX-tabblad, wat de garantie controleerbaar maakt vanuit testcode in plaats van een kwestie van geloof:
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;
Het implementatiedetail dat het waard is om te weten, is dat het vastleggen een herserialisatie op event-niveau is, geen ruwe bytekopie. De streaming XML-lezer van HotXLS toont geen bron-offsets, dus de onbekende subboom wordt opnieuw opgebouwd uit de events Element, Text en EndElement terwijl ze langsstromen. Die aanpak verbergt één klassieke valkuil: een zelfsluitend element zoals <a/> vuurt alleen een leeg-gemarkeerd Element-event af en nooit een EndElement, dus elke diepteteller die uitsluitend bij EndElement afneemt, zal de subboom nooit zien sluiten. Handel dit af en het herbouwde fragment is semantisch gelijk aan het origineel — attribuutaanhalingstekens en zelfsluitende vormen worden genormaliseerd, dus het is niet byte-identiek, maar Excel leest betekenis, geen bytes. Twee eigenschappen van de eigen uitvoer van Excel maken het afspelen veilig: Excel declareert de noodzakelijke xmlns-attributen op of in het element <ext>, zodat elk vastgelegd fragment namespace-zelfvoorzienend is, en diezelfde zelfvoorzienendheid is de reden waarom het dupliceren van een tabblad binnen of tussen werkmappen de vreemde blokken kan meenemen met een eenvoudige string-list toewijzing
calcChain.xml schrijven zodat Excel uw formules vertrouwt
HotXLS schrijft xl/calcChain.xml (het Calculation Chain-onderdeel, ECMA-376 Part 1, §12.3.1) telkens wanneer de opgeslagen werkmap formules bevat, en het kiest hierbij uit twee ordeningen. Als de formule-afhankelijkheidsgrafiek al is gebouwd en actueel is — u heeft Recalculate aangeroepen na uw laatste bewerking — wordt de keten uitgezonden in volledige topologische volgorde, afhankelijkheden voor afhankelijken, met eventuele leden van een circulaire verwijzing aan het einde toegevoegd. Anders worden cellen in documentvolgorde vermeld. Beide zijn correct: Microsofts implementatienotities voor het formaat, [MS-XLSX], behandelen de berekeningsketen als een hint die Excel controleert en herordent tijdens het laden, dus elke complete lijst is legaal, en HotXLS weigert bewust een grafiekopbouw af te dwingen binnen SaveAs — de verhoudingsgewijs hoge complexiteit van de grafiekopbouw is een verborgen kost die u bij het opslaan van grote werkmappen wilt vermijden
Waarom zou u zich druk maken om een onderdeel dat Excel als adviserend behandelt? Omdat de afwezigheid ervan een signaal is. Sommige consumenten — reparatieheuristieken, viewers van derden, diff-tools — verwachten dat een formule-werkmap een berekeningsketen bevat, en een bibliotheek die het onderdeel bij het opslaan stilletjes laat vallen, produceert bestanden die subtiel afwijken van wat Excel schrijft. Het uitzenden van een geldige keten houdt de uitvoer binnen de grenzen van waar de rest van het ecosysteem tegen is getest, wat de stille, onopvallende kern is van round-trip engineering
Waar verliesvrije round-trip eindigt
Eerlijkheid is hier belangrijker dan un vinkje voor marketing, dus de grenzen verdienen evenveel aandacht. HotXLS kopieert niet het hele pakket byte-voor-byte: tabblad-XML, stijlen, gedeelde strings en werkmaponderdelen worden opnieuw gegenereerd op basis van het geparseerde model, dus de uitvoer is semantisch getrouw maar niet binair identiek — alleen al de lokale headers van de ZIP bevatten verse DOS-tijdstempels. Vastgelegde fragmenten <ext> komen genormaliseerd terug, zoals hierboven beschreven. Programmatische overschrijvingen van thema-lettertypen worden genegeerd wanneer er een letterlijk thema aanwezig is. En het behoudsnet heeft een gedefinieerde maaswijdte: functies die HotXLS standaard modelleert (sparklines bijvoorbeeld worden geparseerd en herschreven in plaats van blind gekopieerd) plus externe extLst-inhoud plus de verbatim gecachte onderdelen. Een onderdeel dat noch gemodelleerd is, noch binnen een uitbreidingspunt valt — bijvoorbeeld een aangepast onderdeel van een exotic add-in — valt buiten de drie mechanismen die dit artikel behandelt, dus test uw werkelijke sjablonen in plaats van aannames te doen
Aanvullend behoudswerk maakt het beeld compleet. VBA-projecten en externe werkmapverwijzingen reizen door de opslag heen op basis van dezelfde filosofie van "behoud wat u niet modeleert", behandeld in het bijbehorende artikel over het behoud van VBA en externe koppelingen, en documenteigenschappen in docProps hebben hun eigen lees- en schrijf-API in plaats van stilletjes te worden wegelaten. Wanneer u een spreadsheetbibliotheek evalueert, voer dan de één-cel-test uit: open een functierijke productiewerkmap, verander een enkele waarde, sla op en vergelijk (diff) de uitgepakte onderdelen met het origineel. Wat er is veranderd buiten het tabblad dat u hebt aangeraakt, vertelt u meer over de bibliotheek dan welke functiematrix dan ook
De hier beschreven round-trip mechanismen — letterlijk behoud van het thema sinds v2.89.46, vastleggen van externe extLst en verzending van calcChain.xml sinds v2.131.0 — worden geleverd in het huidige HotXLS Delphi Excel Component, waarvan de productpagina de volledige set van XLSX-lees- en schrijffuncties voor Delphi en C++Builder documenteert