Technisch artikel

HotXLS: protection, page setup, and printing in Delphi

Drie groepen werkbladinstellingen hebben niets te maken met de celwaarden en alles met hoe het bestand zich gedraagt zodra het je code verlaat. Bladbeveiliging bepaalt welke cellen een gebruiker kan bewerken nadat je de werkmap hebt overgedragen. Pagina-instelling legt oriëntatie, papierformaat en marges vast. De afdrukinstellingen (herhaalde titelrijen, schaling en handmatige pagina-einden) bepalen hoe een raster van willekeurige lengte op papier terechtkomt. Geen van de drie is zichtbaar wanneer je de data in een viewer bekijkt, en alle drie falen ze stilletjes in het veld wanneer ze fout staan. HotXLS, een native spreadsheetbibliotheek voor Delphi en C++Builder, biedt het complete oppervlak voor .xls en .xlsx, wat betekent dat het ook elke contra-intuïtieve Excel-regel reproduceert die in dat oppervlak is ingebakken

De eerste van die regels struikelt bijna iedereen de eerste keer dat ze een gegenereerd blad beveiligen. Roep Protect aan en plotseling kan niemand meer in een cel typen, inclusief de invoerkolommen waar je de werkmap omheen hebt gebouwd. Niets in je code raakte die kolommen aan, en dat is precies waarom het gebeurt

Elke cel wordt vergrendeld geboren

ECMA-376 definieert locked als onderdeel van het opmaakrecord van een cel, niet als een eigenschap van de beveiliging zelf, en de standaardwaarde is true. Bladbeveiliging is slechts de schakelaar die de vlag afdwingbaar maakt. Het hele raster draagt dus vanaf het moment dat het bestaat een vergrendelingsvlag, sluimerend, en de aanroep van Protect activeert ze allemaal tegelijk. De oplossing is de volgorde bewust vast te stellen: bouw de lay-out, ontgrendel expliciet de bereiken die gebruikers moeten bewerken, en beveilig als laatste

Diagram van de HotXLS-beschermingsvolgorde in Delphi waarin elke cel geboren wordt met locked true, invoerbereiken eerst met SetLocked worden ontgrendeld, en Sheet.Protect als laatst aangeroepen de ontgrendelde cellen bewerkbaar houdt
Cellen komen vergrendeld binnen als standaard, dus ontgrendel eerst de invoerbereiken en roep Protect als laatste aan om ze bewerkbaar te houden
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... kopregel, naamkolom en tariefformules hier geschreven ...
  Sheet.Range['B2:B50'].SetLocked(False);         // personeel typt hier uren in
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // houd de tariefberekening privé
  Sheet.Protect('review-2026');                   // nu bijten de vergrendelingsvlaggen
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden doet iets aparts en gemakkelijk over het hoofd te zien: terwijl de beveiliging actief is, toont de cel nog steeds zijn berekende waarde, maar de formulebalk toont niets. Dat is van belang wanneer een formule facturatietarieven, marges of scoregewichten bevat die je liever niet aan elke ontvanger geeft die op een totaal klikt. Op de XLS-facade wordt dezelfde bedoeling per bereik uitgedrukt via IXLSRange.Locked en FormulaHidden. Het werkblad daar draagt ook vijftien Allow*-vlaggen (AllowSort, AllowAutoFilter, AllowFormatCells en de rest), zodat een beveiligd blad nog steeds kan worden gesorteerd en gefilterd in plaats van bevroren tot een verzegeld exemplaar

Wat het beveiligingswachtwoord werkelijk beschermt

Beide formaten slaan het beveiligingswachtwoord voor blad en werkmap op als een legacy-hash van 4 hexadecimale cijfers. Zestien bits betekent dat talloze strings botsen met elk gegeven wachtwoord, en verwijdertools zijn slechts één zoekopdracht verwijderd. Behandel beveiliging als een veiligheidsgordel tegen onbedoelde bewerkingen, niet als toegangscontrole. Het is het juiste middel om reviewers te weerhouden van het overtypen van de formulekolom, en het verkeerde middel voor alles waar het woord vertrouwelijk bij komt kijken

Eén niveau hoger vergrendelt ProtectWorkbook op de XLSX-facade de werkmapstructuur, wat het toevoegen, hernoemen, verwijderen of herordenen van bladen voorkomt. Stel dit in wanneer de bladlijst zelf een contract is met een downstream parser die bladen indexeert op naam of positie. Een hernoemd blad breekt de import aan de andere kant net zo zeker als een verwijderde kolom dat doet. De XLS-facade weerspiegelt die gelaagdheid met TXLSWorkbook.Protect op werkmapniveau en per-blad Protect-aanroepen, plus een isProtected-eigenschap voor code die een geërfd bestand moet inspecteren voordat het iets wijzigt

Wanneer de eis échte vertrouwelijkheid is, verandert het mechanisme volledig. SaveAsEncrypted produceert een AES-versleuteld pakket volgens het ECMA-376 Standard Encryption-schema, uitgebreid behandeld in de handleiding voor AES-beveiligde XLSX-uitvoer, en de legacy XLS-facade schrijft en leest RC4-versleutelde .xls-bestanden via EncryptionPassword en de wachtwoordoverload van Open. Het verschil is niet academisch. Een beveiligd blad reist in cleartext, dus elke zip-tool kan de celwaarden lezen, terwijl een versleuteld pakket onleesbaar is zonder het wachtwoord. Een auditregel die zegt "het loonbestand moet beveiligd zijn" betekent bijna altijd versleuteling, welk vocabulaire er ook wordt gebruikt

Diagram dat HotXLS-werkbladbeveiliging, die een 16-bit legacy-hash opslaat en celwaarden in klare tekst achterlaat leesbaar door elke zip-tool, afzet tegen SaveAsEncrypted AES-uitvoer die onleesbaar blijft zonder het wachtwoord
Werkbladbeveiliging is een veiligheidsgordel tegen toevallige bewerkingen terwijl platte-tekst-waarden leesbaar blijven, en alleen AES-versleuteling verbergt de inhoud

Pagina-instelling maakt deel uit van het documentcontract

Afdrukgedrag is onzichtbaar op het scherm, en dat is waarom het zo vaak kapot wordt uitgeleverd. Op het moment dat een klant de werkmap afdrukt, of exporteert naar PDF voor een auditor, veranderen marges, schaling en herhaalde titels in functionele eisen die niemand heeft getest. Op de XLSX-facade hangen deze instellingen rechtstreeks aan het werkblad:

Sheet.PageLandscape := True;
Sheet.PaperSize := xlsxPaperA4;
Sheet.SetPageMargins(0.5, 0.5, 0.75, 0.75, 0.3, 0.3);
Sheet.CenterHeader := 'Monthly Timesheet';
Sheet.RightFooter := 'Page &P of &N';
Sheet.PrintArea := '$A$1:$F$60';     // kale verwijzing: hier geen bladnaam
Sheet.PrintTitleRows := '$1:$1';     // kopregel herhaalt op elke pagina
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // groei naar beneden mee met de data
Sheet.PrintGridlines := False;

Twee van die regels verbergen valkuilen. De kop- en voettekststrings gebruiken Excels opmaakcodes: &P voor de huidige pagina, &N voor het totale aantal, met &L, &C en &R om de drie secties expliciet aan te spreken. De andere valkuil is PrintArea, dat met opzet een kale celverwijzing verwacht. HotXLS slaat deze ongekwalificeerd op en zet de bladnaam ervoor wanneer het het bestand schrijft, dus als je zelf 'Timesheet!$A$1:$F$60' doorgeeft, ontstaat er een dubbel gekwalificeerde, misvormde verwijzing. Dezelfde voorzichtigheid geldt een laag dieper: afdrukgebieden en afdruktitels worden bewaard als de ingebouwde gedefinieerde namen _xlnm.Print_Area en _xlnm.Print_Titles, dus voeg nooit handmatig _xlnm.*-items toe via DefinedNames, anders vechten de twee mechanismen om dezelfde plek

Schaling die productie-datavolumes overleeft

De combinatie FitToWidth := 1 met FitToHeight := 0 leest als "pas de kolommen altijd op één pagina in, en gebruik dan zoveel pagina's naar beneden als de data nodig heeft," en dat is de juiste standaardwaarde voor elk rapport waarvan het aantal rijen varieert. De valkuil is het afstemmen van een vast percentage of een fit-to-page-paar op een testbestand van dertig rijen: geef dezelfde instellingen zeshonderd productierijen en de uitvoer explodeert óf in tientallen afgekapte pagina's óf krimpt tot onder de leesbaarheid. Schaal de breedte, laat de lengte groeien, en herhaal de kopregel via PrintTitleRows zodat pagina zeventien op zichzelf nog steeds leesbaar is

Diagram van HotXLS-afdrukscaling in Delphi met FitToWidth op 1 zodat elke pagina één blad breed blijft, FitToHeight op 0 zodat pagina's naar beneden groeien, PrintTitleRows die de kopbalk herhaalt, en paginabreuken die na ClearAllPageBreaks opnieuw worden gegenereerd
FitToWidth 1 met FitToHeight 0 houdt elke pagina één vel breed terwijl herhaalde titelrijen en geregenereerde einden leesbaarheid behouden

Handmatige einden volgen dezelfde regeneratiediscipline als al het andere in een gegenereerde werkmap. AddRowBreak(BeforeRow) begint een nieuwe pagina vóór een sectiegrens, maar wanneer de generator opnieuw draait en de rijen verschuiven, komt een verouderd pagina-einde midden in de tabel terecht. Roep eerst ClearAllPageBreaks aan, en voeg dan pagina-einden opnieuw toe die zijn berekend uit de eigen rijtellers van de generator in plaats van oude posities te patchen. Op de XLS-facade wonen de equivalente instellingen op Sheet.PageSetup (oriëntatie, papierformaat, marges, kop- en voettekststrings, fit-to-pages), waarbij RepeatRows en RepeatColumns de afdruktitels afdekken

Het resultaat controleren voordat een klant dat doet

Beveiligings- en afdrukfouten delen één eigenschap: ze zijn triviaal om met de hand te verifiëren en worden bijna nooit geverifieerd. Open het gegenereerde bestand in Excel en besteed er negentig seconden aan. Typ in een invoercel en bevestig dat de toetsaanslag wordt geaccepteerd; typ in een vergrendelde cel en bevestig dat de beveiligingsmelding verschijnt; controleer dat een verborgen formule de formulebalk leeg laat. Voer daarna Afdrukvoorbeeld uit op een dataset van productieformaat, geen steekproef van dertig rijen, en lees het aantal pagina's, de herhalende titelrij en de voettekstnummering af. Het voorbeeld is de stap die zichzelf terugbetaalt, omdat de afdrukgeometrie afhangt van instellingen zonder weergave op het scherm, en zonder een fysieke printer is het de enige plek waar een schalingsfout ooit zichtbaar wordt

Eén laatste instelling maakt de review compleet. FreezePane(ACol, ARow) houdt het kopblok in beeld terwijl een reviewer scrolt. Dat is schermgedrag in plaats van afdrukgedrag, maar een reviewer beoordeelt het hele opleverstuk in één keer. En een werkmap die zijn leven begint als een door een ontwerper onderhouden lay-out krijgt het meeste hiervan gratis: de workflow voor sjabloon-rapportgeneratie houdt pagina-instelling in het sjabloon, waar een mens het heeft afgestemd op een echte printer, en laat code de data invullen en de beveiliging opnieuw toepassen zodra de lay-out vaststaat

HotXLS is een native Object Pascal-spreadsheetbibliotheek voor Delphi en C++Builder; de complete API-referentie voor beveiliging en pagina-instelling staat op de productpagina van HotXLS Delphi Component