Files
cc-1c-skills/docs/mxl-dsl-spec.md
T
Nick ShirokovandClaude Opus 5 4005f36cce feat(mxl-compile,mxl-decompile,mxl-validate): примечания к ячейкам
Ключ ячейки note описывает всплывающую подсказку: строкой, объектом
языков или полной формой с оформлением, признаком авторазмера и
геометрией окошка.

Из четырнадцати тегов, которые платформа пишет в примечание, настоящей
информации несут пять. drawingType, pictureSize и id — константы на всём
корпусе. Якорь конца это координаты самой ячейки (1087 примечаний из
1087), якорь начала — 1/1 (1085 из 1087, три исключения в одном макете
берёт раундтрип-ключ anchor). Остаётся текст, стиль, autoSize и четыре
смещения, причём autoSize описывает не наличие геометрии, а пересчёт
размера: при true координаты всё равно записаны и осмысленны.

Стиль примечания — обычная запись палитры; без своего стиля пишем тот,
что даёт Конфигуратор (926 примечаний корпуса из 1087). Формат
примечания добавлен в сбор именованных стилей декомпилятора и в перечень
владельцев формата: иначе стиль подсказки вырезался как неиспользуемый и
ссылка оставалась висячей.

mxl-validate: индекс формата примечания проверяется наравне с ячейкой,
строкой и колонкой.

Стенд СПримечанием собирается байт в байт обоими портами и добавлен в
платформенные фикстуры.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 14:47:23 +03:00

329 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Спецификация MXL DSL — JSON-формат описания табличного документа
Компактный JSON-формат для описания макетов табличных документов 1С (SpreadsheetDocument). Используется навыками `/mxl-compile` (JSON → XML) и `/mxl-decompile` (XML → JSON).
Оформление — шрифты, стили, цвета, рамки, колоночные раскладки — в `mxl-dsl-styles.md`;
полный перечень свойств стиля — в `mxl-dsl-format-properties.md`.
## Пример
```json
{
"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`) |
## Области (`areas[]`)
| Поле | Обяз. | Описание |
|------|:-----:|----------|
| `name` | нет | Имя области для `Макет.ПолучитьОбласть("Имя")` |
| `columnSet` | нет | Ссылка на раскладку из `columnSets` |
| `rows` | да | Массив строк |
Макет собирается из областей — диапазонов подряд идущих строк. Имя делает область именованной: она доступна в коде как `Макет.ПолучитьОбласть("Имя")` и занимает строки своего диапазона. **Область без имени** — просто кусок сетки: так описываются строки, не принадлежащие ни одной именованной области.
## Именованные области координатами (`namedAreas[]`)
Для областей, которые диапазоном подряд идущих строк не описываются: полоса колонок, прямоугольник, ячейка, а также пересекающиеся с другими.
| Поле | Обяз. | Описание |
|------|:-----:|----------|
| `name` | да | Имя области |
| `rows` | \* | Строки: число или диапазон `"N-M"`, 1-based |
| `cols` | \* | Колонки: число или диапазон `"N-M"`, 1-based |
\* Обязательна хотя бы одна из осей.
**Тип области не указывается** — он следует из того, какие оси заданы, как в `ТабличныйДокумент.Область()`: только строки → полоса строк, только колонки → полоса колонок, обе оси → прямоугольник, одиночные значения по обеим осям → одна ячейка.
```json
"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` |
| `"|"` | Продолжение ячейки сверху — увеличивает её `rowspan` |
| `null` | Пустая колонка: позиция занята, ячейка не создаётся |
| `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` |
Объект-элемент трактуется по его ключам: если среди них есть ключ ячейки (`span`, `rowspan`,
`style`, `param`, `detail`, `text`, `template`, `valueType`, `controlType`, `value`,
`control`) — объект
описывает
свойства ячейки. Иначе он целиком считается её текстом, а его ключи — идентификаторами языков.
```json
"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` принимают строку или объект «язык → текст». Объект даёт по надписи на каждый язык, в порядке ключей. Строка означает один и тот же текст на всех языках макета — по умолчанию только русский.
```json
{ "col": 1, "text": "Наименование" }
{ "col": 2, "text": { "ru": "Поставщик", "en": "Supplier" } }
```
Набор языков задаётся документным ключом `textLanguages`:
```json
{ "columns": 3, "textLanguages": ["ru", "en"], "areas": [] }
```
С таким объявлением `"Наименование"` из примера выше даст надпись и под `ru`, и под `en`.
Ключ ни на что в конфигурации не смотрит — это просто список языков, на которые разворачивается строка.
Пустая строка — это текст: ячейка с `"text": ""` даёт пустую надпись, а не ячейку без текста.
## Ячейка-поле ввода
Ячейка может не показывать текст, а принимать значение от пользователя — так делают макеты для
ввода данных. Достаточно задать тип значения:
```json
{ "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чч:мм:сс`
и читается как дата только у ячейки, объявленной датой.
```json
{ "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`
строкой, объектом «язык → текст» или полной формой:
```json
{ "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` независимы: при автоподгоне размера
положение окошка всё равно хранится.
## `rowStyle` — оформление строки
Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком.
Стиль конкретной ячейки (`style`) перекрывает `rowStyle` для этой ячейки.
Если в предыдущих строках той же области есть ячейки с `rowspan`, их колонки при автозаполнении пропускаются.
## Ограничения
DSL описывает не все конструкции табличного документа. Перечисленное ниже **теряется при
round-trip** (`/mxl-decompile``/mxl-compile`): в JSON оно не попадает, в сгенерированный
XML не возвращается.
- объединения, не привязанные к ячейке (по всей высоте или ширине документа);
- рисунки и картинки, в том числе штрихкоды;
- группировки строк и колонок;
- колонтитулы, параметры печати, область печати.
Пересборка макета из DSL — это полная перегенерация, а не точечная правка XML, поэтому
diff после round-trip обычно шире фактической доработки.
Отдельно про побайтовое совпадение. В макетах, которые долго правили в Конфигураторе,
встречаются следы прежних состояний: формат ячейки может нести ширину колонки, которая с тех
пор изменилась. Такие значения не описывают итоговый документ и из него не выводятся, поэтому
собранный XML совпадёт с исходным не всегда — при полностью сохранённом содержании.