Техническая статья

Объекты камеры Excel из Delphi: живые снимки

HotXLS создаёт объекты камеры Excel на листах XLSX из Delphi через AddCamera. Объект камеры — это картинка, постоянно связанная с диапазоном ячеек: Excel перерисовывает её из исходного диапазона при каждом открытии книги или изменении источника, поэтому панель показателей может отображать живой вид таблицы, находящейся на другом листе, в любом размере и, если нужно, повёрнутой

Эта возможность существует в Excel как инструмент «Камера» — кнопка, которую большинство пользователей никогда не видело, поскольку по умолчанию её нет на ленте. Она решает реальную проблему панелей показателей лучше альтернатив: скопированный диапазон устаревает, диаграмма не может показать произвольное содержимое ячеек, а связанная картинка, вставленная вручную, не может быть сгенерирована кодом. Объект камеры — единственная конструкция, одновременно живая и произвольная

Объект камеры HotXLS, созданный из Delphi: живая картинка диапазона Data!$B$2:$D$4, размещённая на листе панели показателей, которую Excel перерисовывает при открытии и при каждой правке источника
Объект камеры остаётся постоянно связанным со своим исходным диапазоном, поэтому Excel перерисовывает картинку при открытии и при каждой правке, в любом размере и из диапазона на другом листе

Где это документировано и почему это важно?

Основная спецификация электронных таблиц не описывает объекты камеры; они трактуются как деталь реализации. Авторитетное описание находится в документации по разметке рисунков, в разделе про инструмент «Камера», и знание этого экономит целый вечер поисков не в том документе

Структурно объект камеры — это обычный элемент картинки, чьи невизуальные свойства картинки несут список расширений. Расширение идентифицируется фиксированным GUID, а внутри него элемент из пространства имён рисунков 2010 года записывает две вещи: исходный диапазон как абсолютную ссылку в стиле A1, опционально с указанием листа, и идентификатор фигуры. Всё остальное в картинке — обычное

Анатомия XLSX объекта камеры, записанного HotXLS из Delphi: обычный элемент картинки, чей список расширений несёт элемент из пространства имён рисунков 2010 года, записывающий абсолютный исходный диапазон и идентификатор фигуры
Структурно камера — это обычная картинка плюс один элемент расширения из пространства имён рисунков 2010 года, записывающий абсолютный исходный диапазон в стиле A1 и идентификатор фигуры

Создание объекта камеры

Существуют две перегрузки, потому что удобны два способа именования диапазона. Текстовая форма принимает ссылку ровно в том виде, в каком она будет сохранена, — это то, что нужно для источника с другого листа. Координатная форма принимает четыре координаты ячеек на том же листе и строит абсолютную ссылку за вас:

uses
  lxHandleX;

var
  Book: TXLSXWorkbook;
  Dashboard: TXLSXWorksheet;
  Cam: TXLSXImage;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('reporting.xlsx') <> 1 then
      Exit;
    Dashboard := Book.Sheets[0];

    // Живой вид Data!$B$2:$D$4, размещённый поверх B10:F20 на панели показателей
    Cam := Dashboard.AddCamera('Data!$B$2:$D$4', 10, 2, 20, 6);

    // Рамка размещения по умолчанию использует собственные столбцы Excel по 64 px
    // и строки по 20 px; при необходимости скорректируйте нарисованный размер в EMU
    Cam.WidthEMU := Round(12.5 * 914400 / 2.54);   // 12,5 см
    Cam.HeightEMU := Round(6.0 * 914400 / 2.54);

    Book.SaveAs('reporting-dashboard.xlsx');
  finally
    Book.Free;
  end;
end;

Возвращаемый объект — это обычный объект картинки с одним дополнительным свойством, CameraRange, которое непусто ровно тогда, когда картинка является камерой. Именно так вы обнаруживаете камеры в книге, которую создавали не вы: перечислите картинки и проверьте это свойство

Почему изображение-заглушка не обязано быть настоящим?

Каждой картинке в пакете XLSX нужна часть изображения, и объект камеры не исключение. Но Excel игнорирует это изображение: при загрузке он перерисовывает связанный диапазон и рисует результат. Встроенные байты существуют исключительно как кэш отображения для инструментов, не реализующих поведение камеры

Этот факт убирает из реализации целую подсистему. Не нужно растрировать исходный диапазон, не нужен движок рендеринга для создания снимка, и нет риска, что кэшированное изображение разойдётся с живым в Excel. HotXLS записывает минимальный метафайл-заглушку — запись заголовка и запись конца файла, всего 108 байт, — что сохраняет структурную целостность пакета и ничего не стоит

Одно следствие, которое стоит учитывать: программа просмотра, отрисовывающая XLSX без реализации объектов камеры, покажет заглушку, а она пустая. Если ваши книги потребляются таким инструментом, объект камеры — неверная конструкция, и отрисованное изображение диапазона — правильная

Часть изображения-заглушки объекта камеры HotXLS: Excel перерисовывает связанный диапазон при загрузке и игнорирует 108-байтовый метафайл, тогда как программы просмотра без поддержки камеры показывают пустую картинку
Excel игнорирует встроенные байты и перерисовывает связанный диапазон при загрузке, поэтому HotXLS записывает только 108-байтовый метафайл-заглушку, а программа просмотра без поддержки камеры показывает пустую заглушку

Преобразование единиц, в котором легко ошибиться

Геометрия рисунков в Open XML измеряется в английских метрических единицах (EMU), где один дюйм — это 914 400 EMU, а один сантиметр — 360 000. Однако заголовки метафайлов записывают свою рамку в единицах по 0,01 миллиметра. Преобразование между ними — это деление на 360

Об этом стоит сказать отдельно, потому что ошибка проходит незаметно. Преобразование через пиксели при 96 DPI вместо этого — умножение на 2540 и деление на 9525 — даёт значение в 96 раз больше нужного, и ничто это не отклоняет: пакет валиден, картинка корректно размещена якорем, и только заявленная рамка метафайла оказывается бессмысленной. Модель EMU и её поведение при округлении описаны в статье геометрия изображений, единицы EMU и масштабирование

Повторное сохранение книги, в которой уже есть камеры

Открытие и сохранение книги сохраняет объекты камеры, включая элемент расширения и его ссылку на диапазон. Это важнее, чем их создание: большинство книг с объектами камеры сделаны в Excel аналитиком, и библиотека, молча превращающая их в обычные картинки при сохранении, разрушает живое поведение, на которое рассчитывал аналитик

// Проверяем, какие картинки являются камерами и куда они указывают
for I := 0 to Sheet.Images.Count - 1 do
  if Sheet.Images[I].CameraRange <> '' then
    Writeln(Format('camera %d -> %s',
      [I, Sheet.Images[I].CameraRange]));

Когда вы генерируете панели показателей программно, эта проверка становится ещё и регрессионным тестом: после цикла сохранения и повторного открытия то же число камер должно указывать на те же диапазоны. Общая обработка картинок и рисунков, включая модель привязки, задающую их позицию, описана в статье диаграммы, изображения и рисунки

Когда камера превосходит альтернативы

Используйте объект камеры, когда одна и та же живая таблица должна появляться на нескольких листах в разных размерах, когда макет печати требует область одного листа, скомпонованную рядом с областями других, или когда сводный блок должен следовать за правками, сделанными в другом месте, без формулы, связывающей каждую ячейку по отдельности

Предпочитайте обычные формулы, когда цель — горстка ячеек, потому что межлистовая формула проще и её понимает любой инструмент. Предпочитайте диаграмму, когда данные — это действительно ряд, а не отформатированный блок. И предпочитайте отрисованное изображение, когда книгу будут потреблять инструменты, отличные от Excel, или когда снимок не должен меняться после поставки — архивный отчёт не должен быть живым

Одно практическое замечание о вёрстке: камера показывает исходный диапазон ровно так, как он отформатирован, включая объединённые ячейки, условное форматирование и ширины столбцов. Получение аккуратного блока панели показателей поэтому начинается с форматирования исходного диапазона так, будто это уже готовый вывод, — та же дисциплина, что описана в статье объединённые ячейки и вёрстка шаблонов отчётов

Объекты камеры, рисунки и стоящий за ними писатель пакета XLSX поставляются в одной библиотеке для Delphi и C++Builder; полный список возможностей — на странице компонента HotXLS Delphi Spreadsheet Component