feat(mxl-compile,mxl-decompile): колонтитулы и параметры печати

Ключи header и footer описывают колонтитул: три слота (left, center,
right), общий шрифт и вертикальное выравнивание, признак вывода и
страница, с которой печатать. Ключ printSettings — плоский набор
параметров печати с именами тегов платформы; порядок компилятор
выставляет сам, незнакомый ключ отвергает.

Слот устроен как ячейка: ссылка на формат плюс текст. Текст бывает двух
видов, и это не текст против шаблона, а обычная строка против
форматированной: у форматированной разметка живёт прямо в содержимом
(<b>жирный</>, <fontsize 12>, <colorstyle -16>), поэтому она возится как
есть. Признак вывода и стартовая страница лежат в формате как <height>
и <width>, но читаются только у записи с <height>: палитра
дедуплицирована, и колонтитул без своих настроек ссылается на чужую
запись, где <width> — ширина колонки (637 таких ссылок против 153 своих).

Перенос строки внутри текста колонтитула платформа хранит голым LF при
CRLF во всём остальном файле — единственное такое место в документе,
поэтому содержимое пишется одной строкой вывода.

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

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-15 17:07:54 +03:00
co-authored by Claude Opus 5
parent 1e0b82d419
commit 702a3b6a8d
16 changed files with 881 additions and 8 deletions
@@ -0,0 +1,10 @@
{
"name": "Ошибка: неизвестный ключ параметров печати",
"input": {
"columns": 1,
"areas": [{ "rows": [["a"]] }],
"printSettings": { "topMargin": 500, "marginTop": 500 }
},
"params": { "outputPath": "Template.xml" },
"expectError": "printSettings: unknown key \"marginTop\""
}
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
<languageInfo>
<id>en</id>
<code>Английский</code>
<description>Английский</description>
</languageInfo>
</languageSettings>
<columns>
<size>1</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Текст</v8:content>
</v8:item>
</tl>
</c>
</c>
</row>
</rowsItem>
<leftHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Слева</v8:content>
</v8:item>
</tl>
</leftHeader>
<centerHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Стр. [&amp;НомерСтраницы] из [&amp;СтраницВсего]. Дата [&amp;Дата][&amp;Время]</v8:content>
</v8:item>
</tl>
</centerHeader>
<rightHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Справа</v8:content>
</v8:item>
</tl>
</rightHeader>
<leftFooter>
<f>3</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Нижний слева</v8:content>
</v8:item>
</tl>
</leftFooter>
<centerFooter>
<f>3</f>
<tfl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>&lt;b&gt;Форматируемая&lt;/&gt; &lt;fontsize 12&gt;&lt;colorstyle -16&gt;строка&lt;/&gt;&lt;/&gt;</v8:content>
</v8:item>
</tfl>
</centerFooter>
<rightFooter>
<f>3</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Нижний справа</v8:content>
</v8:item>
</tl>
</rightFooter>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<printSettings>
<topMargin>1000</topMargin>
<leftMargin>1000</leftMargin>
<bottomMargin>1000</bottomMargin>
<rightMargin>1000</rightMargin>
<headerSize>1200</headerSize>
<footerSize>1000</footerSize>
<firstPageNumber>15</firstPageNumber>
</printSettings>
<font faceName="Arial" height="8" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>72</width>
</format>
<format>
<font>0</font>
<height>-1</height>
<width>2</width>
<verticalAlignment>Center</verticalAlignment>
</format>
<format>
<font>0</font>
<height>1</height>
<width>21</width>
<verticalAlignment>Top</verticalAlignment>
</format>
</document>
@@ -0,0 +1,29 @@
{
"name": "Колонтитулы и параметры печати",
"input": {
"columns": 2,
"fonts": { "мелкий": { "face": "Arial", "size": 8 } },
"areas": [{ "rows": [["Отчёт"]] }],
"header": {
"font": "мелкий",
"verticalAlignment": "Bottom",
"startPage": 2,
"left": "Слева\nвторая строка",
"center": "Стр. [&НомерСтраницы] из [&СтраницВсего]",
"right": { "ru": "Справа", "en": "Right" }
},
"footer": {
"font": "мелкий",
"verticalAlignment": "Top",
"show": false,
"center": { "formatted": "<b>Итог</> <colorstyle -16>красным</>" }
},
"printSettings": {
"pageOrientation": "Landscape",
"topMargin": 500, "leftMargin": 500, "bottomMargin": 500, "rightMargin": 500,
"headerSize": 1000, "footerSize": 1000,
"fitToPage": true, "blackAndWhite": false, "firstPageNumber": 7
}
},
"params": { "outputPath": "Template.xml" }
}
@@ -0,0 +1,13 @@
{
"name": "Раундтрип макета ПЛАТФОРМЫ байт в байт: Колонтитулы",
"setup": "fixture:platform-roundtrip",
"preRun": [
{ "script": "mxl-decompile/scripts/mxl-decompile",
"args": { "-TemplatePath": "Колонтитулы.xml", "-OutputPath": "back.json" },
"cwd": "{workDir}" }
],
"params": { "outputPath": "out.xml" },
"noSnapshot": "эталон — файл платформы, сверяется expect.filesEqual",
"expect": { "filesEqual": { "actual": "out.xml", "expected": "Колонтитулы.xml" } },
"inputFrom": "back.json"
}
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>2</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Отчёт</v8:content>
</v8:item>
</tl>
</c>
</c>
</row>
</rowsItem>
<leftHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Слева
вторая строка</v8:content>
</v8:item>
</tl>
</leftHeader>
<centerHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Стр. [&amp;НомерСтраницы] из [&amp;СтраницВсего]</v8:content>
</v8:item>
</tl>
</centerHeader>
<rightHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Справа</v8:content>
</v8:item>
<v8:item>
<v8:lang>en</v8:lang>
<v8:content>Right</v8:content>
</v8:item>
</tl>
</rightHeader>
<centerFooter>
<f>3</f>
<tfl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>&lt;b&gt;Итог&lt;/&gt; &lt;colorstyle -16&gt;красным&lt;/&gt;</v8:content>
</v8:item>
</tfl>
</centerFooter>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<printSettings>
<pageOrientation>Landscape</pageOrientation>
<topMargin>500</topMargin>
<leftMargin>500</leftMargin>
<bottomMargin>500</bottomMargin>
<rightMargin>500</rightMargin>
<headerSize>1000</headerSize>
<footerSize>1000</footerSize>
<fitToPage>true</fitToPage>
<blackAndWhite>false</blackAndWhite>
<firstPageNumber>7</firstPageNumber>
</printSettings>
<font faceName="Arial" height="8" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>10</width>
</format>
<format>
<font>0</font>
<height>1</height>
<width>2</width>
<verticalAlignment>Bottom</verticalAlignment>
</format>
<format>
<font>0</font>
<height>-1</height>
<width>1</width>
<verticalAlignment>Top</verticalAlignment>
</format>
</document>
@@ -0,0 +1,21 @@
{
"name": "Roundtrip — колонтитулы и параметры печати",
"preRun": [
{ "script": "mxl-compile/scripts/mxl-compile",
"input": {
"columns": 2,
"fonts": { "мелкий": { "face": "Arial", "size": 8 } },
"areas": [{ "rows": [["Отчёт"]] }],
"header": { "font": "мелкий", "verticalAlignment": "Bottom", "startPage": 2,
"left": "Слева\nвторая строка", "center": "Стр. [&НомерСтраницы]" },
"footer": { "font": "мелкий", "show": false,
"center": { "formatted": "<b>Итог</>" } },
"printSettings": { "pageOrientation": "Landscape", "fitToPage": true, "firstPageNumber": 7 }
},
"args": { "-JsonPath": "{inputFile}", "-OutputPath": "Template.xml" },
"cwd": "{workDir}" }
],
"params": { "templatePath": "Template.xml" },
"args_extra": ["-OutputPath", "{workDir}/back.json"],
"expect": { "files": ["back.json"], "stdoutContains": "[OK] Decompiled:" }
}
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>2</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Отчёт</v8:content>
</v8:item>
</tl>
</c>
</c>
</row>
</rowsItem>
<leftHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Слева
вторая строка</v8:content>
</v8:item>
</tl>
</leftHeader>
<centerHeader>
<f>2</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Стр. [&amp;НомерСтраницы]</v8:content>
</v8:item>
</tl>
</centerHeader>
<centerFooter>
<f>3</f>
<tfl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>&lt;b&gt;Итог&lt;/&gt;</v8:content>
</v8:item>
</tfl>
</centerFooter>
<templateMode>true</templateMode>
<defaultFormatIndex>1</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<printSettings>
<pageOrientation>Landscape</pageOrientation>
<fitToPage>true</fitToPage>
<firstPageNumber>7</firstPageNumber>
</printSettings>
<font faceName="Arial" height="8" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
<format>
<width>10</width>
</format>
<format>
<font>0</font>
<height>1</height>
<width>2</width>
<verticalAlignment>Bottom</verticalAlignment>
</format>
<format>
<font>0</font>
<height>-1</height>
<width>1</width>
</format>
</document>
@@ -0,0 +1,10 @@
{
"columns": 2,
"defaultWidth": 10,
"fonts": { "default": { "face": "Arial", "size": 8 } },
"styles": {},
"header": { "font": "default", "startPage": 2, "verticalAlignment": "Bottom", "left": "Слева\nвторая строка", "center": "Стр. [&НомерСтраницы]" },
"footer": { "font": "default", "show": false, "center": { "formatted": "<b>Итог</>" } },
"printSettings": { "pageOrientation": "Landscape", "fitToPage": true, "firstPageNumber": 7 },
"areas": [{ "rows": [["Отчёт"]] }]
}