Technical Article

HotXLS: Защита на листове, настройки на страници и печат в Delphi

Три групи настройки на работни листове нямат нищо общо със стойностите на клетките и имат всичко общо с това как се държи файлът, след като излезе от вашия код. Защитата на листа определя кои клетки може да редактира потребителят, след като му предадете работната книга. Настройките на страницата фиксират ориентацията, размера на хартията и полетата (margins). Настройките за печат (повтарящи се заглавни редове, мащабиране и ръчни прекъсвания на страници) контролират как мрежа с произволна дължина попада на хартия. Нито едно от трите не се вижда, когато просто преглеждате данните на екрана, и трите се провалят тихомълком в практиката, когато са грешни. HotXLS, оригинална библиотека за електронни таблици за Delphi и C++Builder, излага пълната повърхност за .xls и .xlsx, което означава, че тя също така възпроизвежда всяко нелогично правило на Excel, вградено в нея.

Първото от тези правила обърква почти всеки първия път, когато защити генериран лист. Извикайте Protect и изведнъж никой не може да пише в нито една клетка, включително в колоните за въвеждане, около които сте изградили работната книга. Нищо в кода ви не е докосвало тези колони и точно затова се случва това.

Всяка клетка се ражда заключена

ECMA-376 дефинира locked като част от записа за форматиране на клетката, а не като свойство на самата защита, и по подразбиране е true. Защитата на листа е просто превключвателят, който прави флага приложим. Така че цялата мрежа носи флаг за заключване от момента, в който съществува - в спящо състояние, а извикването на Protect активира всички флагове наведнъж. Решението е да зададете реда съзнателно: изградете оформлението, изрично отключете диапазоните, които потребителите трябва да редактират, и защитете накрая.

Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... header row, name column, and rate formulas written here ...
  Sheet.Range['B2:B50'].SetLocked(False);         // staff type hours here
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // keep the rate math private
  Sheet.Protect('review-2026');                   // now the lock flags bite
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

SetFormulaHidden прави нещо отделно и лесно за пренебрегване: докато защитата е активна, клетката все още показва своята изчислена стойност, но лентата за формули не показва нищо. Това е важно, когато формула вгражда ставки за фактуриране, маржове или тегла за оценяване, които не бихте искали да предоставите на всеки получател, кто кликне върху общата сума. Във фасадата XLS същото намерение се изразява за всеки диапазон чрез IXLSRange.Locked и FormulaHidden. Работният лист там също носи петнадесет флага Allow* (AllowSort, AllowAutoFilter, AllowFormatCells и останалите), така че защитеният лист все още да може да се сортира и филтрира, вместо да бъде замразен в запечатан вид.

Какво всъщност защитава паролата за защита

И двата формата съхраняват паролата за защита на листа и работната книга като остарял 4-цифрен хеш. Шестнадесет бита означават, че безброй низове съвпадат с всяка дадена парола, а инструментите за премахване са на едно търсене разстояние. Третирайте защитата като предпазен колан срещу случайни редакции, а не като контрол на достъпа. Тя е правилният инструмент за спиране на потребителите да пишат върху колоната с формули и грешният инструмент за всичко, което включва думата конфиденциално.

Едно ниво нагоре, ProtectWorkbook във фасадата XLSX заключва структурата на работната книга, което предотвратява добавянето, преименуването, изтриването или пренареждането на листове. Задавайте го винаги, когато списъкът с листове сам по себе си е договор с парсер по веригата, който индексира листовете по име или позиция. Преименуваният лист нарушава импортирането от другата страна точно толкова сигурно, колкото и изтритата колона. Фасадата XLS отразява това наслояване с TXLSWorkbook.Protect на ниво работна книга и извиквания на Protect за всеки лист, плюс свойство isProtected за код, който трябва да инспектира наследен файл, преди да модифицира каквото и да било.

Когато изискването е реална поверителност, механизмът се променя изцяло. SaveAsEncrypted произвежда AES-криптиран пакет по схемата ECMA-376 Standard Encryption, описана подробно в ръководството за AES-защитен XLSX изход, а остарялата XLS фасада записва и чете RC4-криптирани .xls файлове чрез EncryptionPassword и претоварването с парола на Open. Разликата не е академична. Защитеният лист се пренася в чист текст, така че всеки zip инструмент може да прочете стойностите на клетките му, докато криптираният пакет е нечетим без паролата. Одиторско изискване, което казва, че "файлът за заплати трябва да бъде защитен", почти винаги означава криптиране, какъвто и термин да се използва.

Настройката на страницата е част от договора за документа

Поведението при печат е невидимо на екрана, поради което толкова често се доставя в неработещ вид. В момента, в който клиент отпечата работната книга или я експортира в PDF за одитор, полетата, мащабирането и повтарящите се заглавия се превръщат във функционални изисквания, които никой не е тествал. Във фасадата XLSX тези настройки са свързани директно с работния лист:

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';     // bare reference: no sheet name here
Sheet.PrintTitleRows := '$1:$1';     // header row repeats on every page
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // grow downward as the data grows
Sheet.PrintGridlines := False;

Два от тези редове крият капани. Низовете за горния и долния колонтитул използват кодовете за форматиране на Excel: &P за текущата страница, &N за общия брой, с &L, &C и &R за изрично адресиране на трите секции. Другият капан е PrintArea, който нарочно приема чиста препратка към клетка. HotXLS го съхранява без квалификация и добавя името на листа като префикс, когато записва файла, так че ако сами подадете 'Timesheet!$A$1:$F$60', това ще доведе до двойно квалифицирана, неправилна препратка. Същата предпазливост се прилага и едно ниво по-долу: печатните области и печатните заглавия се съхраняват като вградените дефинирани имена _xlnm.Print_Area и _xlnm.Print_Titles, така че никога не добавяйте ръчно _xlnm.* записи през DefinedNames, в противен случай двата механизма ще се борят за един и същ слот.

Мащабиране, което издържа на реални производствени обеми данни

Комбинацията FitToWidth := 1 с FitToHeight := 0 се чете като "винаги събирай колоните на една страница, след което използвай толкова страници надолу, колкото изискват данните", и това е правилната настройка по подразбиране за всеки отчет с променлив брой редове. Капанът е да настроите фиксиран процент или двойка за събиране на страници спрямо тестов файл от тридесет реда: подайте същите настройки на шестстотин производствени реда и резултатът или ще се разпадне в десетки отрязани страници, или ще се свие до нечетливост. Мащабирайте ширината, оставете дължината да расте и повторете заглавния ред през PrintTitleRows, така че страница седемнадесет все още да бъде четлива сама по себе си.

Ръчните прекъсвания следват същата дисциплина на регенериране, както всичко останало в генерираната работна книга

AddRowBreak(BeforeRow) започва нова страница преди граница на секция, но когато генераторът се стартира отново и редовете се изместят, остарялото прекъсване се озовава насред таблицата. Първо извикайте ClearAllPageBreaks, след което добавете отново прекъсвания, изчислени от собствените броячи на редове на генератора, вместо да коригирате стари позиции. Във фасадата XLS еквивалентните контроли живеят в Sheet.PageSetup (ориентация, размер на хартията, полета, низове за горен и долен колонтитул, събиране на страници), като RepeatRows и RepeatColumns покриват печатните заглавия.

Проверка на резултата преди клиента

Бъговете със защитата и печата споделят едно общо свойство: те се проверяват елементарно на ръка и почти никога не се проверяват. Отворете генерирания файл в Excel и отделете деветдесет секунди за него. Пишете в клетка за въвеждане и потвърдете, че тя приема въвеждането; пишете в заключена клетка и потвърдете, че се появява подканата за защита; проверете дали скритата формула оставя празна лентата за формули. След това изпълнете предварителен преглед на печата (Print Preview) спрямо производствен обем от данни, а не с мостра от тридесет реда, и отчетете броя на страниците, повтарящия се заглавен ред и номерирането на долния колонтитул. Предварителният преглед е стъпката, която си изплаща усилията, тъй като печатната геометрия зависи от настройки без показване на екрана, и освен физически принтер, това е единственото място, където грешка в мащабирането става видима.

Една последна настройка допълва прегледа. FreezePane(ACol, ARow) запазва заглавния блок в изгледа, докато потребителят скролира. Това е поведение на екрана, а не на печат, но потребителят оценява целия краен продукт наведнъж. А работна книга, която започва живота си като поддържано от дизайнер оформление, получава по-голямата част от това безплатно: процесът за генериране на отчети по шаблони запазва настройката на страницата в шаблона, където човек я е настроил спрямо реален принтер, и оставя кода просто да попълни данните и да приложи защитата отново, след като оформлението се установи.

HotXLS е оригинална Object Pascal библиотека за електронни таблици за Delphi и C++Builder; пълната справочна информация за API за защита и настройка на страници е на страницата на продукта HotXLS Component.