Електронна таблиця несе два шари ідентичності. Є сітка клітинок, і є метадані документа, що їдуть поруч із нею: заголовок, автор, компанія, ключові слова, часові мітки. Excel ніколи не показує цей другий шар у сітці, проте саме цей шар індексує пошук Windows, саме його читає SharePoint, щоб назвати документ, і саме за ним підшиває система керування записами. Коли згенерована книга успадковує свого Автора й Заголовок від шаблону, з якого її побудовано, кожна подальша система записує дизайнера шаблону як автора чотирьох тисяч клієнтських виписок. Метадані не правильні ніде і звіряються всюди
HotXLS відкриває цей шар як звичайні властивості рівня книги в обох своїх рушіях: фасаді BIFF для .xls і фасаді OOXML для .xlsx. Ви читаєте поле після відкриття файлу і записуєте поле перед його збереженням. Бібліотека вирішує, у якому фізичному контейнері опиниться значення. Що варто зрозуміти, перш ніж писати генератор, — це які поля насправді підтримує кожен формат, де ці поля фізично живуть, і те єдине правило-шлагбаум, що визначає, чи запише .xlsx взагалі якісь метадані
Два формати, дві моделі зберігання
Причина, чому бібліотеці електронних таблиць потрібні дві реалізації метаданих, і причина, чому напівготові інструменти проставляють один формат правильно, а про інший забувають, — це те, що .xls і .xlsx тримають свої властивості в непов’язаних місцях. Книга BIFF записує їх у потоки складеного файлу OLE, головно в набір властивостей SummaryInformation, що існував ще до самого Excel, поряд із внутрішньопотоковим записом WRITEACCESS, що називає того, хто останнім зберіг файл. Книга OOXML тримає їх як частини XML усередині пакета zip, розділені за призначенням: docProps/core.xml тримає поля Dublin Core (заголовок, автор, тема, ключові слова, дати), а docProps/app.xml тримає поля рівня застосунку, такі як компанія й програма-генератор, за ECMA-376 Part 1
HotXLS сплющує обидві ці моделі зберігання в прямі властивості об’єкта книги. Ви ніколи не відкриваєте потік набору властивостей і не редагуєте частину XML вручну. Ви присвоюєте рядки й дати книзі, і правильний контейнер матеріалізується для того формату, який ви зберігаєте
Проставлення згенерованих книг з бізнес-запису
У фасаді XLSX TXLSXWorkbook відкриває Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application та AppVersion як рядки, плюс Created і Modified як значення TDateTime, де нуль означає невстановлено. Правило, що закриває дірку успадкування, — одне речення: присвоюйте кожне поле при кожному запуску, беручи значення з бізнес-запису, а не довіряючи тому, що випадково ніс шаблон
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('statement-template.xlsx') <> 1 then
raise Exception.Create('Template not available');
// Перезаписати кожне поле: усе, що лишити неторканим,
// успадковується від того, хто розробив шаблон
Book.Title := 'Account Statement 2026-06 / ACME Corp';
Book.Subject := 'Monthly account statement';
Book.Author := 'Billing Service 4.2';
Book.LastModifiedBy := 'Billing Service 4.2';
Book.Company := 'Northwind Financial';
Book.Category := 'Customer Delivery';
Book.Keywords := 'statement;billing;2026-06;acct-10024';
Book.Description := 'Generated document - manual edits are not retained';
Book.Created := Now;
Book.Modified := Now;
Book.SaveAs('statement-10024.xlsx');
finally
Book.Free;
end;
end;
Поле Keywords заслуговує на більше роздумів, ніж зазвичай отримує. Пошукова інфраструктура індексує його дослівно — Windows Search, SharePoint і більшість продуктів DMS однаково, — тож домовленість із розділенням крапкою з комою, що несе номер рахунку й період, перетворює кожну доставлену книгу на записуваний запис, знайдений без жодного звернення до бази даних. Той самий охоплення — і пастка. Властивості подорожують із кожною копією файлу, далеко за межі контролю доступу системи, що їх написала, тож персональним даним там не місце
Пара часових міток несе семантику, яку варто закріпити в політиці, а не лишати на розсуд звички. Created має позначати момент, коли ваш конвеєр згенерував документ, і після цього лишатися замороженим. Modified — поле, яке Excel оновлює щоразу, коли отримувач зберігає файл, тож розбіжність між ними після доставки — позитивний доказ того, що хтось редагував книгу нижче за течією, що вирішує не одну суперечку про те, чиї числа насправді тримає пересланий файл. Одна пастка ховається в стані «не встановлено»: це буквальне значення нуль, а не виняток і не null, тож код аудиту має явно перевіряти на нуль. Відформатуйте невстановлений TDateTime без цієї перевірки — і ваші логи заповняться впевнено неправильною датою грудня 1899 року
DocPropsTouched: книга, що йде без docProps
Прапорець лише для читання, DocPropsTouched, керує записувачем властивостей XLSX. Книга, у якій жодну властивість ніколи не присвоювали, не видає жодних частин docProps взагалі; HotXLS відмовляється писати порожній скелет метаданих. Поведінка охайна, і в неї є два наслідки, навколо яких варто проєктувати
Код прийому на боці споживача не має припускати, що core.xml існує в кожному пакеті. Інструмент, що жорстко вимагає його, відхилятиме цілком валідні мінімальні файли. А якщо ваша політика відповідності вимагає, щоб кожен вихідний документ ніс щонайменше ідентичність генератора, ця вимога стає кодом, а не властивістю формату: присвоюйте Application і Author безумовно в шляху збереження, бо неторкана книга цілком легальна за специфікацією, при цьому мовчки порушуючи вашу політику
Застаріла поверхня XLS і пастка Comments
Фасад BIFF несе старіший, менший набір полів: Title, Subject, Author, Keywords, Comments, Company і Manager, плюс LastSavedBy, псевдонім UserName, що записує запис WRITEACCESS, який Excel показує, коли інший користувач тримає файл заблокованим
var
Legacy: IXLSWorkbook; // інтерфейс з підрахунком посилань: без ручного Free
begin
Legacy := TXLSWorkbook.Create;
if Legacy.Open('archive-1999.xls') <= 0 then
raise Exception.Create('Cannot open archive file');
Legacy.Title := 'FY1999 ledger (migrated copy)';
Legacy.Author := 'Archive Migration Batch';
Legacy.Company := 'Northwind Financial';
Legacy.Comments := 'Migrated 2026-06-11; source retained in cold storage';
Legacy.LastSavedBy := 'migration-svc'; // запис BIFF WRITEACCESS
Legacy.SaveAs('archive-1999-stamped.xls');
end;
Один конфлікт назв постійно спричиняє плутанину. Властивість рівня документа Comments тут — це довільний текстовий коментар, показаний у діалозі властивостей файлу. Він не має нічого спільного з коментарями клітинок, які є об’єктами шару малюнків, прикріпленими до діапазонів через цілком окремий API. Код-рев’ю, що приймає «ми вже пишемо Comments» без перевірки, який саме мається на увазі, прийняло твердження про неправильну функцію, і це трапляється частіше, ніж можна очікувати від спільної назви. Ці два поняття ділять чотири літери й жодного байта сховища
Читання метаданих при прийомі, і прогалина перевірки
Читання симетричне. Після Open ті самі властивості повертаються заповненими з файлу, що перетворює аудит метаданих вхідних книг на короткий цикл
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open(FileName) = 1 then
begin
Writeln(Format('%s | title="%s" author="%s" created=%s',
[ExtractFileName(FileName), Book.Title, Book.Author,
FormatDateTime('yyyy-mm-dd', Book.Created)]));
if Book.Created = 0 then
Writeln(' no creation date recorded');
end;
finally
Book.Free;
end;
end;
Плануйте навколо одного обмеження, поки це робите. Немає перевірки лише властивостей. GetSheetNames може перелічити аркуші без завантаження книги, але читання Title чи Author означає повний Open, тож тріаж метаданих по великому архіву платить повну вартість розбору на кожному файлі. На боці BIFF ви можете скоротити цю вартість для аудитів лише для читання, встановивши _DisableGraphics в true перед відкриттям, що повністю пропускає шар малюнків. Це пасує циклу, що лише читає властивості й статистику клітинок, і це рівно неправильно тієї миті, коли той самий екземпляр може зберігати, бо пропущений вміст малюнків було б скинуто. Коли сама структура аркушів може попередньо відфільтрувати набір — експорти з одним аркушем є очевидним кандидатом на пропуск, — дешеві техніки в нашій статті про перелічування аркушів і легку перевірку скорочують кількість файлів, що доходять до дорогого проходу. А в масових завданнях проставлення, де тисячі виводів записуються, а не перевіряються, шаблони пропускної здатності на боці запису в нашій статті про потокові записи для пакетних завдань переносяться без змін, бо присвоєння властивостей не додає нічого відчутного до часу збереження
Перетин форматів і стримування витоку
Властивості переносяться туди й назад чисто в межах одного фасаду: відкрийте .xlsx, відредагуйте його, збережіть, і набір повертається неушкодженим. Перетин форматів — це те місце, де припущення про паритет ламається, бо набори полів BIFF і OOXML не вирівнюються один в один. У BIFF є Manager і немає часових міток; у OOXML є Category, Description і пара Created/Modified. Конвертер, що копіює наосліп, втрачає все, що цільовий формат не може вмістити, тож мапуйте поля явно й покладіть це мапування у ваш чек-лист конвертації поряд з усім іншим, що не переживає подорож
Витік, який відкриває успадкування шаблону, працює в інший бік: інформація, яку ви ніколи не мали намір відвантажувати. Імена авторів, внутрішні мітки проєктів, заховані в ключових словах, чорновий заголовок, який ніхто не прибрав. Дисципліна «перезаписати все» з генератора вище — це весь захист, і її варто перевіряти так, як це зробив би сторонній: відкривши діалог властивостей, до якого може дістатися будь-який клієнт, або розпакувавши .xlsx і прочитавши docProps/core.xml прямо з пакета. Те, що ви там бачите, — це рівно те, що бачить кожен подальший індексатор
Ця видимість нижче за течією — також причина, чому кілька полів заслуговують на більше уваги, ніж решта. Title, Author, Keywords (що з’являються як Tags) та Comments чи Description несуть більшу частину індексаційної ваги в SharePoint і Windows Search. Заголовок, що справді унікальний для кожного документа, несе період і рахунок, робить для знаходжуваності більше, ніж будь-яка схема іменування папок, нашарована поверх нього, і коштує одне присвоєння на збереження
Властивості документа — найдешевший професійний штрих, який може нести згенерована книга, і найпоширеніший дефект, що йде у відвантаження, коли ними ніхто не опікується. Обидві поверхні властивостей, описані тут, належать HotXLS Delphi Component, який записує їх нативно для XLS і XLSX без автоматизації Excel