Files
cc-1c-skills/docs/mxl-dsl-spec.md
T
Nick Shirokov 9285ed00cd feat(mxl-compile,mxl-decompile): рисунки и палитра картинок
Рисунок поверх сетки — картинка, фигура, надпись — теперь описывается в DSL
и возвращается из макета: два якоря «ячейка + смещение», тип, ссылка на
картинку, надпись, расшифровка, имя, порядок перекрытия.

Оформление рисунка разложено надвое: общее (заливка, шрифт, выравнивание)
берётся из именованного стиля, а линия и её стороны — собственные ключи
рисунка. У ячейки таких свойств не бывает: все 2 135 записей палитры с ними
принадлежат рисункам.

Палитра картинок: ссылка на библиотеку платформы, данные base64, пустая
запись «картинка не задана» (151 в корпусе) и координата пикселя прозрачного
цвета (tx/ty — проверено, это не размеры картинки).

Заодно: висячий пустой колонтитул в ps1-порте (пустой словарь в PowerShell
истинен, порты расходились на 3 макетах из 40) и проверка индекса формата
рисунка в mxl-validate.

Стенды Рисунки, Рисунки2, КартинкаВЯчейке проходят раундтрип байт в байт
в обоих портах; на 35 корпусных макетах с рисунками расхождение сократилось
у всех 35.
2026-08-15 20:07:12 +03:00

31 KiB
Raw Blame History

Спецификация MXL DSL — JSON-формат описания табличного документа

Компактный JSON-формат для описания макетов табличных документов 1С (SpreadsheetDocument). Используется навыками /mxl-compile (JSON → XML) и /mxl-decompile (XML → JSON).

Оформление — шрифты, стили, цвета, рамки, колоночные раскладки — в mxl-dsl-styles.md; полный перечень свойств стиля — в mxl-dsl-format-properties.md.

Пример

{
  "columns": 10,
  "defaultWidth": 30,
  "columnWidths": { "1": 15, "2-8": 40, "9-10": 50 },

  "fonts": {
    "default": { "face": "Arial", "size": 10 },
    "bold": { "face": "Arial", "size": 10, "bold": true },
    "header": { "face": "Arial", "size": 14, "bold": true }
  },

  "styles": {
    "default": {},
    "header": { "font": "header", "horizontalAlignment": "Center" },
    "label": { "font": "bold" },
    "bordered": { "border": "Solid" },
    "bordered-right": { "border": "Solid", "horizontalAlignment": "Right" },
    "total-right": { "font": "bold", "topBorder": "Solid", "horizontalAlignment": "Right" }
  },

  "areas": [
    {
      "name": "Заголовок",
      "rows": [
        { "height": 20, "cells": [
          { "col": 1, "span": 10, "style": "header", "param": "ТекстЗаголовка" }
        ]}
      ]
    },
    {
      "name": "ШапкаТаблицы",
      "rows": [
        { "rowStyle": "bordered", "cells": [
          { "col": 1, "text": "№" },
          { "col": 2, "span": 6, "text": "Наименование" },
          { "col": 9, "text": "Кол-во" },
          { "col": 10, "text": "Сумма" }
        ]}
      ]
    },
    {
      "name": "Строка",
      "rows": [
        { "rowStyle": "bordered", "cells": [
          { "col": 1, "param": "НомерСтроки" },
          { "col": 2, "span": 6, "param": "Товар", "detail": "Номенклатура" },
          { "col": 9, "style": "bordered-right", "param": "Количество" },
          { "col": 10, "style": "bordered-right", "param": "Сумма" }
        ]}
      ]
    },
    {
      "name": "Итого",
      "rows": [
        { "cells": [
          { "col": 8, "span": 2, "style": "total-right", "text": "Итого:" },
          { "col": 10, "style": "total-right", "param": "Всего" }
        ]}
      ]
    }
  ]
}

Верхний уровень

Поле Обяз. По умолч. Описание
columns да Количество колонок в раскладке по умолчанию. 0 допустимо: значит, все строки живут в раскладках из columnSets
page нет Формат страницы: "A4-landscape" (780), "A4-portrait" (540) или число. Автоматически вычисляет defaultWidth из суммы пропорций "Nx"
defaultWidth нет 10 Ширина колонок по умолчанию. Игнорируется если задан page и все колонки используют "Nx"
columnWidths нет {} Ширины колонок. Ключи 1-based: "1", "3-14", "5,7,9". Значения: число (абсолют) или "Nx" (множитель от defaultWidth, напр. "2x", "0.5x")
columnStyles нет Оформление колонок: те же ключи, значение — имя стиля (см. mxl-dsl-styles.md)
textLanguages нет ["ru"] Языки, на которых пишется текст, заданный строкой (см. ниже)
fonts нет Именованные шрифты (если не задано, создаётся Arial 10)
styles нет {} Именованные стили (см. mxl-dsl-styles.md)
areas да Массив областей — диапазонов подряд идущих строк (порядок = порядок в документе); имя необязательно
namedAreas нет Именованные области, заданные координатами (см. ниже)
columnSets нет Дополнительные колоночные раскладки (см. mxl-dsl-styles.md)
rowGroups нет Группы строк (см. ниже)
columnGroups нет Группы колонок (см. ниже)
header / footer нет Верхний и нижний колонтитулы (см. ниже)
printSettings нет Параметры печати (см. ниже)
pictures нет Палитра картинок: ссылки на библиотеку платформы или данные (см. ниже)
drawings нет Рисунки: картинки, фигуры, надписи (см. ниже)

Области (areas[])

Поле Обяз. Описание
name нет Имя области для Макет.ПолучитьОбласть("Имя")
columnSet нет Ссылка на раскладку из columnSets
rows да Массив строк

Макет собирается из областей — диапазонов подряд идущих строк. Имя делает область именованной: она доступна в коде как Макет.ПолучитьОбласть("Имя") и занимает строки своего диапазона. Область без имени — просто кусок сетки: так описываются строки, не принадлежащие ни одной именованной области.

Именованные области координатами (namedAreas[])

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

Поле Обяз. Описание
name да Имя области
rows * Строки: число или диапазон "N-M", 1-based
cols * Колонки: число или диапазон "N-M", 1-based
columnSet нет Колоночная раскладка области. Без ключа выводится из накрытых строк, "" — привязки нет

* Обязательна хотя бы одна из осей.

Тип области не указывается — он следует из того, какие оси заданы, как в ТабличныйДокумент.Область(): только строки → полоса строк, только колонки → полоса колонок, обе оси → прямоугольник, одиночные значения по обеим осям → одна ячейка.

"namedAreas": [
  { "name": "ОбластьПечатиПоВысоте", "rows": "1-48" },
  { "name": "ОбластьПечатиПоШирине", "cols": "1-35" },
  { "name": "HZY", "rows": 9, "cols": "16-17" }
]

Диапазон — та же грамматика, что у columnWidths, но только число или "N-M": список через запятую запрещён, область непрерывна. Имя обязательно, и хотя бы одна ось должна быть задана; нарушение любого из этих правил → ненулевой код выхода и сообщение в stderr.

Строки (rows[])

Поле По умолч. Описание
height Высота строки (если не задана, используется авто)
hidden false Скрыть строку
rowStyle Стиль строки: ложится и на саму строку, и на ВСЕ её колонки (заполняет пустоты рамками)
cells [] Массив ячеек
empty Количество подряд идущих пустых строк (заменяет N отдельных {})

Строка без cells и rowStyle → пустая строка. { "empty": 3 } эквивалентно трём {}.

height и hidden — собственные свойства строки: у ячейки таких нет, и в её оформление они не попадают. Всё остальное оформление строки задаётся через rowStyle.

Короткая форма: строка массивом

Вместо объекта строка может быть массивом ячеек — позиция определяется порядком, col не указывается.

Элемент Значение
"текст" Статический текст (text)
{ "ru": "…", "en": "…" } Тот же текст на нескольких языках
"{Имя}" Параметр (param)
">" Продолжение ячейки слева — увеличивает её span
`" "`
null Пустая колонка: позиция занята, ячейка не создаётся
{ ... } Обычная ячейка без col; нужна для style, detail, template

Объект-элемент трактуется по его ключам: если среди них есть ключ ячейки (span, rowspan, style, param, detail, text, template, valueType, controlType, value, control) — объект описывает свойства ячейки. Иначе он целиком считается её текстом, а его ключи — идентификаторами языков.

"rows": [
  ["Вид", "Остаток", ">", "Итог"],
  ["|",   "начало",  "конец", "|"],
  ["{Вид}", "{Нач}", "{Кон}", "{Итог}"]
]

Здесь «Вид» и «Итог» объединены по вертикали на две строки, «Остаток» — по горизонтали на две колонки.

Ограничения короткой формы:

  • не задать height и rowStyle — это свойства строки, а не ячейки;
  • не выразить текст, совпадающий с ">", "|" или с шаблоном "{...}".

Маркеру нужно, что продолжать: ">" требует ячейку слева в той же строке, "|" — ячейку сверху. Объектный элемент не должен нести col: позиция уже задана порядком. Число элементов не может превышать columns. Нарушение любого из этих правил → ненулевой код выхода и сообщение в stderr.

Ячейки (cells[])

Поле Обяз. По умолч. Описание
col да Позиция колонки (1-based). В короткой форме строки не указывается — позиция берётся из порядка
span нет 1 Объединение по горизонтали (количество колонок)
rowspan нет 1 Объединение по вертикали (количество строк)
style нет rowStyle Стиль ячейки (переопределяет rowStyle)
param нет Параметр заполнения
detail нет Параметр расшифровки. Самостоятельный ключ: ставится и на ячейку без param — с текстом, пустую или поле ввода
text нет Статический текст. Строка или объект { ru, en } — см. ниже
template нет Шаблонный текст с [Параметр]. Строка или объект, как text
valueType нет Тип значения: ячейка становится полем ввода (см. ниже)
controlType нет input Элемент управления поля ввода: input или checkbox. Только вместе с valueType
value нет Значение в поле ввода. Только вместе с valueType
control нет Настройки элемента управления в записи платформы (base64). Раундтрип, не для ручного авторинга
note нет Примечание к ячейке (см. ниже)

Содержимое ячейки

Задаётся ровно одним из ключей, объявлять способ заполнения отдельно не нужно:

  • param — параметр заполнения;
  • template — текст со вставками [Параметр];
  • text — статический текст;
  • ничего — пустая ячейка (нужна, например, ради рамки).

Текст на нескольких языках

text и template принимают строку или объект «язык → текст». Объект даёт по надписи на каждый язык, в порядке ключей. Строка означает один и тот же текст на всех языках макета — по умолчанию только русский.

{ "col": 1, "text": "Наименование" }
{ "col": 2, "text": { "ru": "Поставщик", "en": "Supplier" } }

Набор языков задаётся документным ключом textLanguages:

{ "columns": 3, "textLanguages": ["ru", "en"], "areas": [] }

С таким объявлением "Наименование" из примера выше даст надпись и под ru, и под en.

Ключ ни на что в конфигурации не смотрит — это просто список языков, на которые разворачивается строка.

Пустая строка — это текст: ячейка с "text": "" даёт пустую надпись, а не ячейку без текста.

Ячейка-поле ввода

Ячейка может не показывать текст, а принимать значение от пользователя — так делают макеты для ввода данных. Достаточно задать тип значения:

{ "col": 1, "valueType": "Number(15,3,nonneg)" }
{ "col": 2, "valueType": "String(10)" }
{ "col": 3, "valueType": "Boolean", "controlType": "checkbox" }
{ "col": 4, "valueType": "CatalogRef.Валюты" }
{ "col": 5, "valueType": "Boolean + String + Date + CatalogRef.Валюты" }
{ "col": 6, "valueType": "AnyRef" }

Грамматика типа:

Запись Значение
Boolean булево
String, String(10), String(10,fixed) строка; без длины — неограниченная, fixed — фиксированной длины
Number, Number(15,3), Number(15,3,nonneg) число; без параметров — без ограничения разрядности, nonneg — неотрицательное
Date, DateTime, Time дата, дата со временем, время
CatalogRef.Валюты, DocumentRef.Реализация, EnumRef.Статусы, DefinedType.Сумма ссылочный тип
CatalogRef, DocumentRef, AnyRef категория целиком: любая ссылка этого вида
A + B составной тип; повтор одного примитива недопустим

Русские имена принимаются наравне с английскими: Число(15,3), Строка(10), СправочникСсылка.Валюты.

text и template в такой ячейке недопустимы — платформа не показывает текст там, где вводится значение. param и detail допустимы: они описывают заполнение и расшифровку, а не содержимое.

Пустой тип "valueType": "" — «ячейка содержит значение», но тип не ограничен.

Значение поля ввода задаётся ключом value, и его тип выражается литералом JSON: строка — строкой, число — числом, булево — true/false. Приведения к объявленному типу не происходит: платформа хранит значение с его собственным типом и совпадать с объявленным он не обязан (у ссылочного и составного типа значение всегда строковое). Пустая строка означает пустое значение объявленного типа. Дата записывается строкой в формате ГГГГ-ММ-ДДTчч:мм:сс и читается как дата только у ячейки, объявленной датой.

{ "col": 1, "valueType": "Number(15,3)", "value": 12.5 }
{ "col": 2, "valueType": "String(10)", "value": "5" }
{ "col": 3, "valueType": "Boolean", "value": true }
{ "col": 4, "valueType": "DateTime", "value": "" }

controlType нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, включая Boolean. Флажок задаётся явно. Значение "none" (тега элемента управления нет вовсе) — форма раундтрипа, для ручного авторинга не нужна.

Примечание к ячейке

Всплывающая подсказка, которую платформа показывает при наведении. Задаётся ключом note — строкой, объектом «язык → текст» или полной формой:

{ "col": 1, "text": "Итого", "note": "Сумма без НДС" }
{ "col": 2, "note": { "ru": "на дату документа", "en": "as of the document date" } }
{ "col": 3, "note": { "text": "не более 20%", "style": "жёлтая-подсказка" } }
{ "col": 4, "note": { "text": "…", "autoSize": false,
                      "box": { "top": 58, "left": -175, "bottom": 362, "right": 478 } } }

Объект трактуется по ключам — так же, как текст ячейки в короткой форме строки: есть ключ примечания (text, style, box, autoSize, anchor) → это описание примечания, иначе ключи считаются идентификаторами языков.

Поле По умолч. Описание
text Текст подсказки: строка или объект «язык → текст»
style стиль подсказки Имя стиля из styles; без него — оформление, которое даёт Конфигуратор
autoSize true Подгонять ли размер окошка под текст
box канонический Смещения окошка: top, left — положение, bottom, right — размер
anchor { row: 1, col: 1 } Якорь начала окошка. Раундтрип, не для ручного авторинга

Координаты ячейки в примечании не задаются — платформа привязывает конец окошка к самой ячейке, и компилятор проставляет это сам. autoSize и box независимы: при автоподгоне размера положение окошка всё равно хранится.

Колонтитулы

У верхнего и нижнего колонтитула по три слота — left, center, right — и общие настройки:

"header": {
  "font": "мелкий",
  "verticalAlignment": "Bottom",
  "startPage": 2,
  "left": "Слева\nвторая строка",
  "center": "Стр. [&НомерСтраницы] из [&СтраницВсего]",
  "right": { "ru": "Справа", "en": "Right" }
},
"footer": {
  "show": false,
  "center": { "formatted": "<b>Итог</> <colorstyle -16>красным</>" }
}
Поле По умолч. Описание
left, center, right Текст слота: строка, объект «язык → текст» или { "formatted": … }
font Имя шрифта из fonts — на весь колонтитул
verticalAlignment Положение текста по вертикали: Top, Center, Bottom
show true Выводить ли колонтитул
startPage 1 Страница, с которой колонтитул печатается

Текст может быть многострочным (\n) и содержать поля [&НомерСтраницы], [&СтраницВсего], [&Дата], [&Время] — платформа подставляет их при печати. { "formatted": … } — форматированная строка: разметка живёт прямо в тексте (<b>жирный</>, <fontsize 12>, <colorstyle -16>).

Параметры печати

Плоский объект; имя ключа совпадает с именем свойства в выгрузке:

"printSettings": {
  "pageOrientation": "Landscape",
  "topMargin": 500, "leftMargin": 500, "bottomMargin": 500, "rightMargin": 500,
  "headerSize": 1000, "footerSize": 1000,
  "fitToPage": true, "firstPageNumber": 7
}

Допустимые ключи: pageOrientation, scale, collate, copies, perPage, topMargin, leftMargin, bottomMargin, rightMargin, headerSize, footerSize, fitToPage, blackAndWhite, printerName, paper, paperSource, pageWidth, pageHeight, duplexType, pagePlacementAlternation, firstPageNumber. Незнакомый ключ — ошибка. Порядок в объекте не важен: компилятор пишет свойства в том порядке, что и платформа.

Группы строк и колонок

Диапазон строк или колонок, который сворачивается кнопкой на полях. Задаются плоским списком; вложенность выражена вхождением одного диапазона в другой:

"rowGroups": [
  { "rows": "2-4" },
  { "rows": 3 },
  { "rows": 5, "name": "Итоговая", "collapsed": true, "titleLocation": "begin" }
],
"columnGroups": [
  { "cols": "2-3", "name": { "ru": "Показатели", "en": "Values" } },
  { "cols": 3 }
]
Поле Обяз. По умолч. Описание
rows / cols да Диапазон 1-based: число или "N-M"
name нет Имя группы: строка или объект «язык → текст»
collapsed нет false Свёрнута ли группа при открытии
titleLocation нет auto Расположение заголовка: begin, end, auto

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

Рисунки и картинки

Рисунок — объект поверх сетки ячеек: картинка, фигура или надпись. Его положение задают два якоря: ячейка плюс смещение в точках от её левого верхнего угла.

"pictures": {
  "знак":  { "ref": "v8ui:Стоп48" },
  "логотип": { "data": "iVBORw0KGgo...", "transparent": false }
},
"drawings": [
  { "type": "Picture", "picture": "логотип", "name": "Логотип",
    "begin": { "row": 1, "col": 1 },
    "end":   { "row": 3, "col": 2, "dy": 34, "dx": 115 },
    "pictureSize": "Proportionally", "detail": "ПараметрРасшифровки" },
  { "type": "Rectangle", "style": "заливка",
    "line": { "style": "Dashed", "width": 2 },
    "sides": { "left": true, "top": true, "right": false, "bottom": false },
    "begin": { "row": 5, "col": 1 }, "end": { "row": 6, "col": 3 } },
  { "type": "Text", "text": { "ru": "Подпись", "en": "Signature" },
    "begin": { "row": 8, "col": 1 }, "end": { "row": 8, "col": 3, "dy": 12 } }
]
Поле Обяз. По умолч. Описание
type нет Picture Picture, Rectangle, Ellipse, Line, Text, Chart, GanttChart
begin / end да Якоря: row, col (1-based) и смещения dy, dx в точках
picture нет Имя записи из pictures
pictureSize нет Stretch Как картинка заполняет прямоугольник: Stretch, AutoSize, Proportionally, RealSize
text нет Надпись: строка или объект «язык → текст»
name нет Имя рисунка, по которому к нему обращаются из кода
detail нет Параметр расшифровки
style нет Именованный стиль: заливка, шрифт, выравнивание надписи
line нет Линия рисунка (она же его рамка) — как border у ячейки
sides нет Какие стороны рамки видны: left, top, right, bottom
id нет номер по порядку Идентификатор рисунка
zOrder нет номер по порядку Порядок перекрытия: чем больше, тем выше

Порядок в списке drawings — порядок в документе; кто кого перекрывает, задаёт zOrder.

Запись в pictures — либо ссылка на библиотеку картинок платформы (ref), либо сами данные в base64 (data). Пустая запись {} — картинка не задана, такое в макетах встречается. С данными сочетаются transparent (прозрачность включена) и transparentPixel ({ "x": …, "y": … } — координата пикселя, чей цвет считается прозрачным).

Одну запись pictures могут использовать несколько рисунков — данные в макете не дублируются.

rowStyle — оформление строки

Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком.

Стиль конкретной ячейки (style) перекрывает rowStyle для этой ячейки.

Если в предыдущих строках той же области есть ячейки с rowspan, их колонки при автозаполнении пропускаются.

Ограничения

DSL описывает не все конструкции табличного документа. Перечисленное ниже теряется при round-trip (/mxl-decompile/mxl-compile): в JSON оно не попадает, в сгенерированный XML не возвращается.

  • объединения, не привязанные к ячейке (по всей высоте или ширине документа);
  • настройки диаграмм (Chart, GanttChart) — сам рисунок сохраняется, его содержимое нет;
  • область печати.

Пересборка макета из DSL — это полная перегенерация, а не точечная правка XML, поэтому diff после round-trip обычно шире фактической доработки.

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