Technický článek

HotXLS: Ochrana listů, nastavení stránky a tisk v Delphi

Tři skupiny nastavení listu nemají nic společného s hodnotami buněk a vše společné s tím, jak se soubor chová ve chvíli, kdy opustí váš kód. Ochrana listu rozhoduje, které buňky smí uživatel editovat poté, co sešit předáte. Nastavení stránky určuje orientaci, velikost papíru a okraje. Nastavení tisku (opakované řádky titulků, měřítko a ruční zalomení stránek) řídí, jak mřížka libovolné délky přistane na papíře. Žádná z těchto tří věcí se neprojeví, když si data prohlédnete pohledem ve viewer, a všechny tři se v terénu tiše rozbijí, když jsou špatně. HotXLS, nativní knihovna tabulkových procesorů pro Delphi a C++Builder, zpřístupňuje kompletní povrch pro .xls i .xlsx, což znamená, že reprodukuje i každé neintuitivní pravidlo Excelu zapečené v tomto povrchu

První z těchto pravidel podrazí nohy skoro každému, kdo poprvé zabezpečí vygenerovaný list. Zavoláte Protect, a najednou nikdo nemůže psát do žádné buňky, včetně vstupních sloupců, kolem kterých jste sešit postavili. Vašeho kódu se tyto sloupce nikdy nedotkly, a přesně proto se to stane

Každá buňka se rodí uzamčená

ECMA-376 definuje locked jako součást formátovacího záznamu buňky, ne jako vlastnost samotné ochrany, a výchozí hodnota je true. Ochrana listu je pouhý přepínač, který tento příznak učiní vymahatelným. Celá mřížka tedy nese příznak zámku od okamžiku své existence, spící, a volání Protect je všechny naráz aktivuje. Oprava spočívá v záměrném nastavení pořadí: postavte rozvržení, výslovně odemkněte rozsahy, které uživatelé musí editovat, a zabezpečte až jako poslední krok

Diagram pořadí ochrany HotXLS v Delphi, kde každá buňka se rodí s locked true, vstupní rozsahy se nejdřív odemknou přes SetLocked a Sheet.Protect zavolané naposled nechá odemknuté buňky upravitelné
Buňky přicházejí zamčené, takže nejdřív odemkněte vstupní rozsahy a Protect volejte poslední, aby zůstaly upravitelné
Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... zde se zapisuje řádek záhlaví, sloupec názvu a vzorce sazby ...
  Sheet.Range['B2:B50'].SetLocked(False);         // sem zaměstnanci zadávají hodiny
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // udrž výpočet sazby v soukromí
  Sheet.Protect('review-2026');                   // teď se příznaky zámku uplatní
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden dělá něco samostatného a snadno přehlédnutelného: dokud je ochrana aktivní, buňka stále zobrazuje svou vypočítanou hodnotu, ale řádek vzorců nezobrazuje nic. To hraje roli, když vzorec vkládá fakturační sazby, marže nebo váhy skórování, které byste raději nesvěřovali každému příjemci, jenž klikne na součet. Na fasádě XLS se stejný záměr vyjadřuje na úrovni rozsahu přes IXLSRange.Locked a FormulaHidden. List tam navíc nese patnáct příznaků Allow* (AllowSort, AllowAutoFilter, AllowFormatCells a zbytek), takže zabezpečený list se stále dá třídit a filtrovat místo aby zůstal zmražený jako zapečetěný exponát

Co heslo ochrany skutečně chrání

Oba formáty ukládají heslo ochrany listu a sešitu jako starší hash o 4 hexadecimálních číslicích. Šestnáct bitů znamená, že s daným heslem koliduje bezpočet řetězců, a nástroje na odstranění jsou vzdálené jedno vyhledávání. Zacházejte s ochranou jako s bezpečnostním pásem proti náhodným úpravám, ne jako s řízením přístupu. Je to správný nástroj na to, aby recenzenti nepřepisovali sloupec vzorců, a špatný nástroj pro cokoli, co zahrnuje slovo důvěrné

O úroveň výš ProtectWorkbook na fasádě XLSX uzamkne strukturu sešitu, což zabrání přidávání, přejmenovávání, mazání nebo přeuspořádávání listů. Nastavte ji vždy, když je samotný seznam listů smlouvou s navazujícím parserem, který indexuje listy podle jména nebo pozice. Přejmenovaný list rozbije import na druhé straně stejně jistě jako smazaný sloupec. Fasáda XLS toto vrstvení zrcadlí pomocí TXLSWorkbook.Protect na úrovni sešitu a volání Protect na úrovni jednotlivých listů, plus vlastnost isProtected pro kód, který potřebuje prozkoumat zděděný soubor dřív, než cokoli upraví

Když je požadavkem skutečná důvěrnost, mechanismus se změní úplně. SaveAsEncrypted vytvoří balíček šifrovaný AES podle schématu ECMA-376 Standard Encryption, podrobně popsaného v průvodci šifrovaným výstupem XLSX pomocí AES, a starší fasáda XLS zapisuje a čte soubory .xls šifrované RC4 přes EncryptionPassword a heslovou variantu Open. Ten rozdíl není akademický. Zabezpečený list cestuje v čitelném textu, takže jeho hodnoty buněk dokáže přečíst jakýkoli nástroj na zip, zatímco šifrovaný balíček je bez hesla nečitelný. Auditní řádek, který říká „soubor s mzdami musí být chráněný", téměř vždy znamená šifrování, ať už používá jakoukoli terminologii

Diagram kontrastující ochranu listu HotXLS ukládající 16bitový legacy hash a nechávající hodnoty buněk v otevřeném textu čitelné jakýmkoli zip nástrojem s výstupem SaveAsEncrypted AES, který bez hesla zůstává nečitelný
Ochrana listu je bezpečnostní pás proti náhodným úpravám, zatímco hodnoty v čitelném textu zůstávají čitelné, a jen šifrování AES skryje obsah

Nastavení stránky je součástí smlouvy o dokumentu

Chování při tisku je na obrazovce neviditelné, a proto se tak často dodává rozbité. Ve chvíli, kdy zákazník sešit vytiskne nebo jej exportuje do PDF pro auditora, se okraje, měřítko a opakované titulky promění ve funkční požadavky, které nikdo netestoval. Na fasádě XLSX visí tato nastavení přímo na listu:

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';     // holá reference: bez názvu listu
Sheet.PrintTitleRows := '$1:$1';     // řádek záhlaví se opakuje na každé stránce
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // roste dolů, jak rostou data
Sheet.PrintGridlines := False;

Dva z těchto řádků skrývají pasti. Řetězce záhlaví a zápatí používají formátovací kódy Excelu: &P pro aktuální stránku, &N pro celkový počet, s &L, &C a &R pro explicitní adresování tří sekcí. Druhá past je PrintArea, která záměrně přebírá holou referenci na buňku. HotXLS ji ukládá bez kvalifikace a při zápisu souboru přidá prefix s názvem listu, takže pokud sami předáte 'Timesheet!$A$1:$F$60', vznikne dvojitě kvalifikovaná, poškozená reference. Stejná opatrnost platí o úroveň níž: oblasti a titulky tisku se persistují jako vestavěné definované názvy _xlnm.Print_Area a _xlnm.Print_Titles, takže nikdy nepřidávejte položky _xlnm.* přes DefinedNames ručně, jinak si oba mechanismy budou soupeřit o stejný slot

Měřítko, které přežije produkční objemy dat

Kombinace FitToWidth := 1 s FitToHeight := 0 se čte jako „vždy vejdi sloupce na jednu stránku a pak vezmi tolik stránek dolů, kolik data potřebují", a je to správný výchozí stav pro jakýkoli report, jehož počet řádků se mění. Past je ladit pevné procento nebo dvojici fit-to-page proti testovacímu souboru s třiceti řádky: nakrmíte stejná nastavení šesti sty produkčními řádky a výstup buď exploduje do desítek oříznutých stránek, nebo se zmenší pod hranici čitelnosti. Škálujte šířku, nechte délku růst a opakujte řádek záhlaví přes PrintTitleRows, aby byla stránka sedmnáct čitelná i sama o sobě

Diagram tiskového škálování HotXLS v Delphi s FitToWidth nastaveným na 1, takže každá strana zůstane jeden list široká, FitToHeight nastaveným na 0, takže strany rostou dolů, PrintTitleRows opakujícím pásem záhlaví a zalomeními stran znovu generovanými po ClearAllPageBreaks
FitToWidth 1 s FitToHeight 0 drží každou stránku jeden list širokou, zatímco opakované titulní řádky a znovu generované zlomy zachovávají čitelnost

Ruční zalomení se řídí stejnou disciplínou regenerace jako všechno ostatní ve vygenerovaném sešitu. AddRowBreak(BeforeRow) zahájí novou stránku před hranicí sekce, ale když generátor běží znovu a řádky se posunou, zastaralé zalomení přistane uprostřed tabulky. Nejprve zavolejte ClearAllPageBreaks a pak znovu přidejte zalomení vypočítaná z vlastních počítadel řádků generátoru místo opravování starých pozic. Na fasádě XLS žijí ekvivalentní ovládací prvky na Sheet.PageSetup (orientace, velikost papíru, okraje, řetězce záhlaví a zápatí, fit-to-pages), přičemž RepeatRows a RepeatColumns pokrývají titulky tisku

Kontrola výsledku dřív, než ji provede zákazník

Chyby v ochraně a tisku sdílejí jednu vlastnost: je triviální je ověřit ručně, a skoro nikdy se neověřují. Otevřete vygenerovaný soubor v Excelu a strávte na něm devadesát sekund. Zapište do vstupní buňky a potvrďte, že stisk klávesy přijme; zapište do uzamčené buňky a potvrďte, že se objeví výzva ochrany; zkontrolujte, že skrytý vzorec nechává řádek vzorců prázdný. Pak spusťte náhled tisku na datové sadě produkční velikosti, ne na vzorku o třiceti řádcích, a odečtěte počet stránek, opakující se řádek titulku a číslování v zápatí. Náhled je krok, který se vyplácí, protože geometrie tisku závisí na nastaveních bez jakéhokoli vykreslení na obrazovce, a bez fyzické tiskárny je to jediné místo, kde se chyba v měřítku vůbec projeví

Jedno poslední nastavení kontrolu zaokrouhlí. FreezePane(ACol, ARow) udržuje blok záhlaví v zorném poli, zatímco recenzent scrolluje. To je chování obrazovky, ne chování tisku, ale recenzent posuzuje celou dodávku najednou. A sešit, který začíná život jako rozvržení udržované návrhářem, dostane většinu tohoto zdarma: workflow generování reportů ze šablon drží nastavení stránky v šabloně, kde jej člověk vyladil proti skutečné tiskárně, a kódu nechává jen naplnění dat a opětovné použití ochrany, jakmile se rozvržení usadí

HotXLS je nativní knihovna tabulkových procesorů v Object Pascalu pro Delphi a C++Builder; kompletní referenci API pro ochranu a nastavení stránky najdete na stránce produktu HotXLS Delphi Component