Три групи налаштувань аркуша не мають нічого спільного зі значеннями клітинок і мають все спільне з тим, як файл поводиться, щойно покидає ваш код. Захист аркуша визначає, які клітинки користувач може редагувати після того, як ви передасте книгу. Параметри сторінки фіксують орієнтацію, розмір паперу та поля. Налаштування друку (повторювані рядки заголовків, масштабування та ручні розриви сторінок) керують тим, як сітка довільної довжини лягає на папір. Жодна з трьох не проявляється, коли ви на око оглядаєте дані у переглядачі, і всі три тихо ламаються на практиці, коли налаштовані неправильно. HotXLS, нативна бібліотека електронних таблиць для Delphi та C++Builder, відкриває повну поверхню для .xls і .xlsx, а це означає, що вона також відтворює кожне контрінтуїтивне правило Excel, вбудоване в цю поверхню
Перше з цих правил заплутує майже кожного, хто вперше захищає згенерований аркуш. Викличте Protect — і раптом ніхто не може набирати текст у жодній клітинці, включно зі стовпцями введення, навколо яких ви побудували книгу. Ваш код не торкався тих стовпців нічим, і саме тому це й трапляється
Кожна клітинка народжується заблокованою
ECMA-376 визначає locked як частину запису форматування клітинки, а не як властивість самого захисту, і за замовчуванням це значення істинне. Захист аркуша — лише перемикач, що робить цей прапорець дієвим. Тож уся сітка несе прапорець блокування з моменту свого існування, у сплячому стані, а виклик Protect активує їх усі одразу. Виправлення — свідомо визначити порядок дій: побудувати макет, явно розблокувати діапазони, які користувачі мають редагувати, і захистити останнім
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Timesheet');
// ... тут записано рядок заголовка, стовпець імен і формули ставок ...
Sheet.Range['B2:B50'].SetLocked(False); // тут персонал вводить години
Sheet.Range['F2:F50'].SetFormulaHidden(True); // тримати обчислення ставок приватними
Sheet.Protect('review-2026'); // тепер прапорці блокування діють
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 записує й читає файли .xls, зашифровані RC4, через 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'; // гола адреса: без імені аркуша тут
Sheet.PrintTitleRows := '$1:$1'; // рядок заголовка повторюється на кожній сторінці
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0; // росте вниз разом із даними
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 і витратьте на нього дев’яносто секунд. Наберіть текст у клітинці введення й переконайтеся, що вона приймає натискання клавіш; наберіть текст у заблокованій клітинці й переконайтеся, що з’являється підказка захисту; перевірте, що прихована формула лишає рядок формул порожнім. Потім запустіть попередній перегляд друку на наборі даних виробничого розміру, а не на тридцятирядковому зразку, і зчитайте кількість сторінок, рядок заголовка, що повторюється, і нумерацію підвалу. Попередній перегляд — той крок, що окупається сам, бо геометрія друку залежить від налаштувань без екранного відображення, і, крім фізичного принтера, це єдине місце, де помилка масштабування взагалі стає видимою
Останнє налаштування завершує огляд. FreezePane(ACol, ARow) тримає блок заголовка у видимій зоні, поки рецензент прокручує аркуш. Це поведінка екрана, а не поведінка друку, але рецензент оцінює весь результат одразу. А книга, що починає своє життя як макет, підтримуваний у дизайнері, отримує більшість цього безкоштовно: робочий процес генерації звітів за шаблонами тримає параметри сторінки в шаблоні, де їх налаштувала людина проти реального принтера, і залишає коду лише заповнення даних і повторне застосування захисту, щойно макет усталюється
HotXLS — нативна бібліотека електронних таблиць Object Pascal для Delphi та C++Builder; повний довідник API щодо захисту та параметрів сторінки — на сторінці продукту HotXLS Delphi Component