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>
This commit is contained in:
Nick Shirokov
2026-08-15 14:47:23 +03:00
co-authored by Claude Opus 5
parent faa8ab7659
commit 4005f36cce
17 changed files with 1071 additions and 14 deletions
+2 -1
View File
@@ -59,7 +59,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J
styles: { name: { font, horizontalAlignment, verticalAlignment, textPlacement, styles: { name: { font, horizontalAlignment, verticalAlignment, textPlacement,
backColor, textColor, border, borderColor, format, hidden } }, backColor, textColor, border, borderColor, format, hidden } },
areas: [{ name, columnSet, rows: [{ height, hidden, rowStyle, cells: [ areas: [{ name, columnSet, rows: [{ height, hidden, rowStyle, cells: [
{ col, span, rowspan, style, param, detail, text, template, valueType, controlType, value } { col, span, rowspan, style, param, detail, text, template, valueType, controlType, value, note }
]}]}], ]}]}],
namedAreas: [{ name, rows, cols }], namedAreas: [{ name, rows, cols }],
columnSets: { name: { columns, columnWidths, columnStyles } } columnSets: { name: { columns, columnWidths, columnStyles } }
@@ -80,6 +80,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J
- `col` — 1-based позиция колонки - `col` — 1-based позиция колонки
- `rowspan` — объединение строк вниз (rowStyle учитывает занятые ячейки) - `rowspan` — объединение строк вниз (rowStyle учитывает занятые ячейки)
- Содержимое ячейки задаётся одним из ключей: `param` — параметр заполнения, `text` — статический текст, `template` — текст со вставками `[Параметр]` - Содержимое ячейки задаётся одним из ключей: `param` — параметр заполнения, `text` — статический текст, `template` — текст со вставками `[Параметр]`
- `note` — всплывающая подсказка у ячейки: строка, объект языков или `{ text, style, autoSize, box }`
- `valueType` делает ячейку полем ввода (`"Number(15,3,nonneg)"`, `"String(10)"`, `"CatalogRef.Валюты"`, составной через ` + `); текста в такой ячейке быть не может. `value` — значение в поле, `controlType: "checkbox"` — флажок вместо поля ввода - `valueType` делает ячейку полем ввода (`"Number(15,3,nonneg)"`, `"String(10)"`, `"CatalogRef.Валюты"`, составной через ` + `); текста в такой ячейке быть не может. `value` — значение в поле, `controlType: "checkbox"` — флажок вместо поля ввода
Двухуровневая шапка массивами: Двухуровневая шапка массивами:
@@ -152,7 +152,8 @@
| `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` | | `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` |
Объект-элемент трактуется по его ключам: если среди них есть ключ ячейки (`span`, `rowspan`, Объект-элемент трактуется по его ключам: если среди них есть ключ ячейки (`span`, `rowspan`,
`style`, `param`, `detail`, `text`, `template`, `valueType`, `controlType`, `value`) — объект `style`, `param`, `detail`, `text`, `template`, `valueType`, `controlType`, `value`,
`note`) — объект
описывает описывает
свойства ячейки. Иначе он целиком считается её текстом, а его ключи — идентификаторами языков. свойства ячейки. Иначе он целиком считается её текстом, а его ключи — идентификаторами языков.
@@ -186,6 +187,7 @@
| `valueType` | нет | — | Тип значения: ячейка становится полем ввода (см. ниже) | | `valueType` | нет | — | Тип значения: ячейка становится полем ввода (см. ниже) |
| `controlType` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` | | `controlType` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` |
| `value` | нет | — | Значение в поле ввода. Только вместе с `valueType` | | `value` | нет | — | Значение в поле ввода. Только вместе с `valueType` |
| `note` | нет | — | Примечание к ячейке (см. ниже) |
### Содержимое ячейки ### Содержимое ячейки
@@ -265,6 +267,34 @@
`controlType` нужен редко: умолчание платформы — поле ввода, и оно подходит всем типам, включая `controlType` нужен редко: умолчание платформы — поле ввода, и оно подходит всем типам, включая
`Boolean`. Флажок задаётся явно. `Boolean`. Флажок задаётся явно.
## Примечание к ячейке
Всплывающая подсказка, которую платформа показывает при наведении. Задаётся ключом `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`) → это описание примечания, иначе ключи
считаются идентификаторами языков.
| Поле | По умолч. | Описание |
|------|-----------|----------|
| `text` | — | Текст подсказки: строка или объект «язык → текст» |
| `style` | стиль подсказки | Имя стиля из `styles`; без него — оформление, которое даёт Конфигуратор |
| `autoSize` | `true` | Подгонять ли размер окошка под текст |
| `box` | канонический | Смещения окошка: `top`, `left` — положение, `bottom`, `right` — размер |
Координаты ячейки в примечании не задаются — платформа привязывает конец окошка к самой ячейке,
и компилятор проставляет это сам. `autoSize` и `box` независимы: при автоподгоне размера
положение окошка всё равно хранится.
## `rowStyle` — оформление строки ## `rowStyle` — оформление строки
Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком. Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком.
@@ -280,7 +310,7 @@ round-trip** (`/mxl-decompile` → `/mxl-compile`): в JSON оно не попа
XML не возвращается. XML не возвращается.
- объединения, не привязанные к ячейке (по всей высоте или ширине документа); - объединения, не привязанные к ячейке (по всей высоте или ширине документа);
- рисунки и картинки, в том числе штрихкоды, и примечания к ячейкам; - рисунки и картинки, в том числе штрихкоды;
- группировки строк и колонок; - группировки строк и колонок;
- колонтитулы, параметры печати, область печати. - колонтитулы, параметры печати, область печати.
@@ -1,4 +1,4 @@
# mxl-compile v1.42 — Compile 1C spreadsheet from JSON # mxl-compile v1.43 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -933,6 +933,60 @@ function Get-CellValue {
return @{ Type = 'xs:string'; Text = '' } return @{ Type = 'xs:string'; Text = '' }
} }
# Примечание к ячейке. Из четырнадцати тегов, которые пишет платформа, настоящей информации
# несут пять: текст, стиль, признак авторазмера и четыре смещения окошка. Остальное — константы
# (drawingType, pictureSize, id) либо выводится: якорь конца это координаты самой ячейки
# (1087 примечаний корпуса, без исключений), якорь начала — 1/1 (1085 из 1087).
$script:noteKeys = @('text', 'style', 'box', 'autoSize', 'anchor')
# Стиль подсказки, который даёт Конфигуратор: 926 примечаний корпуса из 1087.
$script:noteDefaultStyle = [ordered]@{
verticalAlignment = 'Top'
textColor = 'style:ToolTipTextColor'
backColor = 'style:ToolTipBackColor'
}
# Размер окошка платформа подбирает по тексту, и вычислить его мы не можем. Пишем самый
# частый набор корпуса; при autoSize он всё равно пересчитывается при показе.
$script:noteDefaultBox = @{ Top = -21; Left = 21; Bottom = 51; Right = 408 }
function Test-NoteObject {
param($el)
foreach ($p in $el.PSObject.Properties) {
if ($script:noteKeys -contains $p.Name) { return $true }
}
return $false
}
function Get-CellNote {
param($cell, [string]$where)
$raw = $cell.note
if ($null -eq $raw) { return $null }
# Как у текста ячейки: строка — текст, объект трактуется по ключам. Ключи примечания
# и идентификаторы языков не пересекаются.
$text = $raw
$style = $null
$autoSize = $true
$box = @{} + $script:noteDefaultBox
$anchor = @{ Row = 1; Col = 1 }
if ($raw -is [System.Management.Automation.PSCustomObject] -and (Test-NoteObject $raw)) {
$text = $raw.text
if ($null -eq $text) { $text = '' }
if ($raw.style) { $style = "$($raw.style)" }
if ($null -ne $raw.autoSize) { $autoSize = ($raw.autoSize -eq $true -or "$($raw.autoSize)" -eq 'true') }
foreach ($side in @('Top', 'Left', 'Bottom', 'Right')) {
$v = $raw.box.$side
if ($null -ne $v) { $box[$side] = [int]$v }
}
if ($raw.anchor) {
if ($null -ne $raw.anchor.row) { $anchor.Row = [int]$raw.anchor.row }
if ($null -ne $raw.anchor.col) { $anchor.Col = [int]$raw.anchor.col }
}
}
return @{ Text = $text; Style = $style; AutoSize = $autoSize; Box = $box; Anchor = $anchor }
}
# --- 6. Format palette builder --- # --- 6. Format palette builder ---
$formatRegistry = [ordered]@{} # key -> hashtable with properties $formatRegistry = [ordered]@{} # key -> hashtable with properties
@@ -1153,6 +1207,59 @@ function Get-FillType {
return "" return ""
} }
# Формат примечания — обычная запись палитры: без своего стиля берём канонический стиль подсказки.
function Register-NoteFormat {
param($note)
if ($note.Style) {
$props = Resolve-Style -styleName $note.Style -fillType ""
} else {
$props = @{}
foreach ($k in $script:noteDefaultStyle.Keys) { $props[$k] = $script:noteDefaultStyle[$k] }
}
return Register-Format $props
}
# Порядок тегов внутри <note> снят с корпуса: у всех 1087 примечаний он один и тот же,
# и все четырнадцать тегов присутствуют всегда.
function Emit-CellNote {
param($note, [int]$fmtIdx, [int]$row, [int]$col)
X "`t`t`t`t`t<note>"
X "`t`t`t`t`t`t<drawingType>Comment</drawingType>"
X "`t`t`t`t`t`t<id>0</id>"
X "`t`t`t`t`t`t<formatIndex>$fmtIdx</formatIndex>"
$pairs = @()
if ($note.Text -is [System.Collections.IDictionary]) {
foreach ($k in $note.Text.Keys) { $pairs += @{ Lang = "$k"; Text = "$($note.Text[$k])" } }
} elseif ($note.Text -is [System.Management.Automation.PSCustomObject]) {
foreach ($p in $note.Text.PSObject.Properties) { $pairs += @{ Lang = $p.Name; Text = "$($p.Value)" } }
} else {
foreach ($l in $textLanguages) { $pairs += @{ Lang = $l; Text = "$($note.Text)" } }
}
if ($pairs.Count -eq 0) {
X "`t`t`t`t`t`t<text/>"
} else {
X "`t`t`t`t`t`t<text>"
foreach ($p in $pairs) {
X "`t`t`t`t`t`t`t<v8:item>"
X "`t`t`t`t`t`t`t`t<v8:lang>$($p.Lang)</v8:lang>"
X "`t`t`t`t`t`t`t`t<v8:content>$(Esc-XmlText $p.Text)</v8:content>"
X "`t`t`t`t`t`t`t</v8:item>"
}
X "`t`t`t`t`t`t</text>"
}
X "`t`t`t`t`t`t<beginRow>$($note.Anchor.Row)</beginRow>"
X "`t`t`t`t`t`t<beginRowOffset>$($note.Box.Top)</beginRowOffset>"
X "`t`t`t`t`t`t<endRow>$row</endRow>"
X "`t`t`t`t`t`t<endRowOffset>$($note.Box.Bottom)</endRowOffset>"
X "`t`t`t`t`t`t<beginColumn>$($note.Anchor.Col)</beginColumn>"
X "`t`t`t`t`t`t<beginColumnOffset>$($note.Box.Left)</beginColumnOffset>"
X "`t`t`t`t`t`t<endColumn>$col</endColumn>"
X "`t`t`t`t`t`t<endColumnOffset>$($note.Box.Right)</endColumnOffset>"
X "`t`t`t`t`t`t<autoSize>$(if ($note.AutoSize) { 'true' } else { 'false' })</autoSize>"
X "`t`t`t`t`t`t<pictureSize>Stretch</pictureSize>"
X "`t`t`t`t`t</note>"
}
# Helper: register a cell format and return its index # Helper: register a cell format and return its index
function Register-CellFormat { function Register-CellFormat {
param($styleName, [string]$fillType, [hashtable]$valueProps) param($styleName, [string]$fillType, [hashtable]$valueProps)
@@ -1188,7 +1295,7 @@ function Set-CellProp {
function Test-CellObject { function Test-CellObject {
param($el) param($el)
$cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template', $cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template',
'valueType', 'controlType', 'value', 'control') 'valueType', 'controlType', 'value', 'control', 'note')
foreach ($p in $el.PSObject.Properties) { foreach ($p in $el.PSObject.Properties) {
if ($cellKeys -contains $p.Name) { return $true } if ($cellKeys -contains $p.Name) { return $true }
} }
@@ -1371,6 +1478,8 @@ foreach ($area in $def.areas) {
$ft = Get-FillType $cell $ft = Get-FillType $cell
$vp = Get-CellValueProps $cell "area `"$($area.name)`"" $vp = Get-CellValueProps $cell "area `"$($area.name)`""
Register-CellFormat -styleName $cellStyle -fillType $ft -valueProps $vp | Out-Null Register-CellFormat -styleName $cellStyle -fillType $ft -valueProps $vp | Out-Null
$note = Get-CellNote $cell "area `"$($area.name)`""
if ($note) { Register-NoteFormat $note | Out-Null }
} }
} }
} }
@@ -1653,6 +1762,8 @@ foreach ($area in $def.areas) {
$ft = Get-FillType $cell $ft = Get-FillType $cell
$vp = Get-CellValueProps $cell "area `"$areaName`", row $($localRow + 1)" $vp = Get-CellValueProps $cell "area `"$areaName`", row $($localRow + 1)"
$fmtIdx = Register-CellFormat -styleName $cellStyle -fillType $ft -valueProps $vp $fmtIdx = Register-CellFormat -styleName $cellStyle -fillType $ft -valueProps $vp
$cellNote = Get-CellNote $cell "area `"$areaName`", row $($localRow + 1)"
$cellNoteFmt = if ($cellNote) { Register-NoteFormat $cellNote } else { 0 }
$cellInfo = @{ $cellInfo = @{
Col = $colStart - 1 # 0-based Col = $colStart - 1 # 0-based
@@ -1663,6 +1774,8 @@ foreach ($area in $def.areas) {
Template = $cell.template Template = $cell.template
Value = $(if ($vp.Count -gt 0) { Get-CellValue $cell "$($vp['valueType'])" "area `"$areaName`", row $($localRow + 1)" } else { $null }) Value = $(if ($vp.Count -gt 0) { Get-CellValue $cell "$($vp['valueType'])" "area `"$areaName`", row $($localRow + 1)" } else { $null })
Control = $(if ($vp.Count -gt 0) { $cell.control } else { $null }) Control = $(if ($vp.Count -gt 0) { $cell.control } else { $null })
Note = $cellNote
NoteFmt = $cellNoteFmt
} }
$rowCells += $cellInfo $rowCells += $cellInfo
@@ -1793,6 +1906,12 @@ foreach ($area in $def.areas) {
X "`t`t`t`t`t<detailParameter>$($cellInfo.Detail)</detailParameter>" X "`t`t`t`t`t<detailParameter>$($cellInfo.Detail)</detailParameter>"
} }
# Якорь конца примечания — координаты самой ячейки, поэтому его не задают:
# он выводится здесь, при эмиссии.
if ($null -ne $cellInfo.Note) {
Emit-CellNote $cellInfo.Note $cellInfo.NoteFmt $globalRow $cellInfo.Col
}
X "`t`t`t`t</c>" X "`t`t`t`t</c>"
X "`t`t`t</c>" X "`t`t`t</c>"
} }
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# mxl-compile v1.42 — Compile 1C spreadsheet from JSON # mxl-compile v1.43 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import hashlib import hashlib
@@ -644,6 +644,58 @@ def emit_value_type_content(lines, indent, canon_type):
lines.extend(quals[q]) lines.extend(quals[q])
# Примечание к ячейке. Из четырнадцати тегов, которые пишет платформа, настоящей информации
# несут пять: текст, стиль, признак авторазмера и четыре смещения окошка. Остальное — константы
# (drawingType, pictureSize, id) либо выводится: якорь конца это координаты самой ячейки
# (1087 примечаний корпуса, без исключений), якорь начала — 1/1 (1085 из 1087).
NOTE_KEYS = ('text', 'style', 'box', 'autoSize', 'anchor')
# Стиль подсказки, который даёт Конфигуратор: 926 примечаний корпуса из 1087.
NOTE_DEFAULT_STYLE = {
'verticalAlignment': 'Top',
'textColor': 'style:ToolTipTextColor',
'backColor': 'style:ToolTipBackColor',
}
# Размер окошка платформа подбирает по тексту, и вычислить его мы не можем. Пишем самый
# частый набор корпуса; при autoSize он всё равно пересчитывается при показе.
NOTE_DEFAULT_BOX = {'top': -21, 'left': 21, 'bottom': 51, 'right': 408}
def is_note_object(el):
return any(k in el for k in NOTE_KEYS)
def cell_note(cell, where):
"""Как у текста ячейки: строка — текст, объект трактуется по ключам. Ключи примечания
и идентификаторы языков не пересекаются."""
raw = cell.get('note')
if raw is None:
return None
text = raw
style = None
auto_size = True
box = dict(NOTE_DEFAULT_BOX)
anchor = {'row': 1, 'col': 1}
if isinstance(raw, dict) and is_note_object(raw):
text = raw.get('text')
if text is None:
text = ''
if raw.get('style'):
style = str(raw.get('style'))
if raw.get('autoSize') is not None:
auto_size = raw.get('autoSize') is True or str(raw.get('autoSize')).lower() == 'true'
raw_box = raw.get('box') or {}
for side in ('top', 'left', 'bottom', 'right'):
if raw_box.get(side) is not None:
box[side] = int(raw_box.get(side))
raw_anchor = raw.get('anchor') or {}
for k in ('row', 'col'):
if raw_anchor.get(k) is not None:
anchor[k] = int(raw_anchor.get(k))
return {'Text': text, 'Style': style, 'AutoSize': auto_size, 'Box': box, 'Anchor': anchor}
def cell_value(cell, canon_type, where): def cell_value(cell, canon_type, where):
"""Значение ячейки-поля ввода — тег САМОЙ ячейки (<v>), а не запись палитры: у двух ячеек """Значение ячейки-поля ввода — тег САМОЙ ячейки (<v>), а не запись палитры: у двух ячеек
с одинаковым оформлением значения разные, и в дедупликацию формата оно не входит. с одинаковым оформлением значения разные, и в дедупликацию формата оно не входит.
@@ -1174,6 +1226,46 @@ def main():
return 'Template' return 'Template'
return '' return ''
# Формат примечания — обычная запись палитры: без своего стиля берём канонический стиль подсказки.
def register_note_format(note):
props = resolve_style(note['Style'], '') if note['Style'] else dict(NOTE_DEFAULT_STYLE)
return register_format(props)
def emit_cell_note(lines, note, fmt_idx, row, col):
"""Порядок тегов внутри <note> снят с корпуса: у всех 1087 примечаний он один и тот же,
и все четырнадцать тегов присутствуют всегда."""
lines.append('\t\t\t\t\t<note>')
lines.append('\t\t\t\t\t\t<drawingType>Comment</drawingType>')
lines.append('\t\t\t\t\t\t<id>0</id>')
lines.append(f'\t\t\t\t\t\t<formatIndex>{fmt_idx}</formatIndex>')
value = note['Text']
if isinstance(value, dict):
pairs = [(str(k), str(v)) for k, v in value.items()]
else:
pairs = [(lang, str(value)) for lang in text_languages]
if not pairs:
lines.append('\t\t\t\t\t\t<text/>')
else:
lines.append('\t\t\t\t\t\t<text>')
for lang, content in pairs:
lines.append('\t\t\t\t\t\t\t<v8:item>')
lines.append(f'\t\t\t\t\t\t\t\t<v8:lang>{lang}</v8:lang>')
lines.append(f'\t\t\t\t\t\t\t\t<v8:content>{esc_xml_text(content)}</v8:content>')
lines.append('\t\t\t\t\t\t\t</v8:item>')
lines.append('\t\t\t\t\t\t</text>')
box = note['Box']
lines.append(f'\t\t\t\t\t\t<beginRow>{note["Anchor"]["row"]}</beginRow>')
lines.append(f'\t\t\t\t\t\t<beginRowOffset>{box["top"]}</beginRowOffset>')
lines.append(f'\t\t\t\t\t\t<endRow>{row}</endRow>')
lines.append(f'\t\t\t\t\t\t<endRowOffset>{box["bottom"]}</endRowOffset>')
lines.append(f'\t\t\t\t\t\t<beginColumn>{note["Anchor"]["col"]}</beginColumn>')
lines.append(f'\t\t\t\t\t\t<beginColumnOffset>{box["left"]}</beginColumnOffset>')
lines.append(f'\t\t\t\t\t\t<endColumn>{col}</endColumn>')
lines.append(f'\t\t\t\t\t\t<endColumnOffset>{box["right"]}</endColumnOffset>')
lines.append('\t\t\t\t\t\t<autoSize>%s</autoSize>' % ('true' if note['AutoSize'] else 'false'))
lines.append('\t\t\t\t\t\t<pictureSize>Stretch</pictureSize>')
lines.append('\t\t\t\t\t</note>')
# Helper: register a cell format and return its index # Helper: register a cell format and return its index
def register_cell_format(style_name, fill_type, value_props=None): def register_cell_format(style_name, fill_type, value_props=None):
resolved = resolve_style(style_name, fill_type) resolved = resolve_style(style_name, fill_type)
@@ -1366,6 +1458,9 @@ def main():
ft = get_fill_type(cell) ft = get_fill_type(cell)
vp = cell_value_props(cell, f'area "{area.get("name") or ""}"') vp = cell_value_props(cell, f'area "{area.get("name") or ""}"')
register_cell_format(cell_style, ft, vp) register_cell_format(cell_style, ft, vp)
note = cell_note(cell, f'area "{area.get("name") or ""}"')
if note:
register_note_format(note)
# Формат по умолчанию — последняя запись палитры (см. выше). # Формат по умолчанию — последняя запись палитры (см. выше).
default_format_index = register_format({'width': default_width}) default_format_index = register_format({'width': default_width})
@@ -1607,6 +1702,8 @@ def main():
ft = get_fill_type(cell) ft = get_fill_type(cell)
vp = cell_value_props(cell, f'area "{area_name}", row {local_row + 1}') vp = cell_value_props(cell, f'area "{area_name}", row {local_row + 1}')
fmt_idx = register_cell_format(cell_style, ft, vp) fmt_idx = register_cell_format(cell_style, ft, vp)
note = cell_note(cell, f'area "{area_name}", row {local_row + 1}')
note_fmt = register_note_format(note) if note else 0
cell_info = { cell_info = {
'Col': col_start - 1, # 0-based 'Col': col_start - 1, # 0-based
@@ -1618,6 +1715,8 @@ def main():
'Value': (cell_value(cell, vp.get('valueType', ''), 'Value': (cell_value(cell, vp.get('valueType', ''),
f'area "{area_name}", row {local_row + 1}') if vp else None), f'area "{area_name}", row {local_row + 1}') if vp else None),
'Control': (cell.get('control') if vp else None), 'Control': (cell.get('control') if vp else None),
'Note': note,
'NoteFmt': note_fmt,
} }
row_cells.append(cell_info) row_cells.append(cell_info)
@@ -1737,6 +1836,12 @@ def main():
if cell_info['Detail']: if cell_info['Detail']:
lines.append(f'\t\t\t\t\t<detailParameter>{cell_info["Detail"]}</detailParameter>') lines.append(f'\t\t\t\t\t<detailParameter>{cell_info["Detail"]}</detailParameter>')
# Якорь конца примечания — координаты самой ячейки, поэтому его не задают:
# он выводится здесь, при эмиссии.
if cell_info.get('Note') is not None:
emit_cell_note(lines, cell_info['Note'], cell_info['NoteFmt'],
global_row, cell_info['Col'])
lines.append('\t\t\t\t</c>') lines.append('\t\t\t\t</c>')
lines.append('\t\t\t</c>') lines.append('\t\t\t</c>')
@@ -1,4 +1,4 @@
# mxl-decompile v1.22 — Decompile 1C spreadsheet to JSON # mxl-decompile v1.23 — Decompile 1C spreadsheet to JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -417,6 +417,39 @@ foreach ($riNode in $root.SelectNodes("d:rowsItem", $ns)) {
$value = @{ Type = $vNode.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance"); Text = $vNode.InnerText } $value = @{ Type = $vNode.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance"); Text = $vNode.InnerText }
} }
# Примечание к ячейке. Якоря не читаем как данные: конец — координаты самой
# ячейки, начало — 1/1 у 1085 примечаний корпуса из 1087; остальное авторское.
$note = $null
$noteNode = $cContent.SelectSingleNode("d:note", $ns)
if ($noteNode) {
$noteText = [ordered]@{}
$tEl = $noteNode.SelectSingleNode("d:text", $ns)
if ($tEl) {
foreach ($it in $tEl.SelectNodes("v8:item", $ns)) {
$l = $it.SelectSingleNode("v8:lang", $ns)
$cnt = $it.SelectSingleNode("v8:content", $ns)
$noteText[$(if ($l) { $l.InnerText } else { '' })] = $(if ($cnt) { $cnt.InnerText } else { '' })
}
}
$ng = @{}
foreach ($ch in $noteNode.ChildNodes) {
if ($ch.NodeType -eq [System.Xml.XmlNodeType]::Element) { $ng[$ch.get_LocalName()] = $ch.InnerText.Trim() }
}
$note = @{
FormatIdx = [int]($(if ($ng['formatIndex']) { $ng['formatIndex'] } else { 0 }))
Text = $noteText
AutoSize = ($(if ($ng.ContainsKey('autoSize')) { $ng['autoSize'] } else { 'true' }) -ceq 'true')
Box = [ordered]@{
top = [int]($(if ($ng['beginRowOffset']) { $ng['beginRowOffset'] } else { 0 }))
left = [int]($(if ($ng['beginColumnOffset']) { $ng['beginColumnOffset'] } else { 0 }))
bottom = [int]($(if ($ng['endRowOffset']) { $ng['endRowOffset'] } else { 0 }))
right = [int]($(if ($ng['endColumnOffset']) { $ng['endColumnOffset'] } else { 0 }))
}
AnchorRow = [int]($(if ($ng['beginRow']) { $ng['beginRow'] } else { 1 }))
AnchorCol = [int]($(if ($ng['beginColumn']) { $ng['beginColumn'] } else { 1 }))
}
}
# Настройки элемента управления — сериализованный base64 у самой ячейки. # Настройки элемента управления — сериализованный base64 у самой ячейки.
# Структуру не разбираем, возим дословно. # Структуру не разбираем, возим дословно.
$control = $null $control = $null
@@ -460,6 +493,7 @@ foreach ($riNode in $root.SelectNodes("d:rowsItem", $ns)) {
Detail = $detail Detail = $detail
Value = $value Value = $value
Control = $control Control = $control
Note = $note
Text = $text Text = $text
HasText = $hasText HasText = $hasText
} }
@@ -646,6 +680,16 @@ foreach ($r in ($rowData.Keys | Sort-Object { [int]$_ } | ForEach-Object { $rowD
$key = Get-StyleKey $fmt $key = Get-StyleKey $fmt
if (-not $styleKeys.Contains($key)) { $styleKeys[$key] = $fmt } if (-not $styleKeys.Contains($key)) { $styleKeys[$key] = $fmt }
$formatToStyleKey[$cell.FormatIdx] = $key $formatToStyleKey[$cell.FormatIdx] = $key
# Формат примечания живёт в той же палитре и тоже заслуживает имени: иначе
# оформление подсказки терялось бы при обратной сборке.
if ($cell.Note) {
$nfmt = Get-Format $cell.Note.FormatIdx
if ($nfmt) {
$nkey = Get-StyleKey $nfmt
if (-not $styleKeys.Contains($nkey)) { $styleKeys[$nkey] = $nfmt }
$formatToStyleKey[$cell.Note.FormatIdx] = $nkey
}
}
} }
} }
@@ -1074,7 +1118,7 @@ foreach ($area in $blocks) {
$hasValue = ($cf -and $cf.Props['containsValue'] -ceq 'true') $hasValue = ($cf -and $cf.Props['containsValue'] -ceq 'true')
# Расшифровка сама по себе делает ячейку содержательной: в корпусе 12 653 ячейки # Расшифровка сама по себе делает ячейку содержательной: в корпусе 12 653 ячейки
# несут только её. Без этого такая ячейка уходила в заполнители и терялась. # несут только её. Без этого такая ячейка уходила в заполнители и терялась.
$hasContent = $cell.Param -or $cell.HasText -or $hasValue -or $cell.Detail $hasContent = $cell.Param -or $cell.HasText -or $hasValue -or $cell.Detail -or $cell.Note
$hasMerge = $mergeMap.ContainsKey("$globalRow,$($cell.Col)") $hasMerge = $mergeMap.ContainsKey("$globalRow,$($cell.Col)")
if ($hasContent -or $hasMerge) { if ($hasContent -or $hasMerge) {
@@ -1193,6 +1237,20 @@ foreach ($area in $blocks) {
# с параметром, две трети расшифровок терялись молча. # с параметром, две трети расшифровок терялись молча.
if ($cell.Detail) { $dslCell["detail"] = $cell.Detail } if ($cell.Detail) { $dslCell["detail"] = $cell.Detail }
if ($cell.Note) {
$n = $cell.Note
$dslNote = [ordered]@{}
$dslNote["text"] = Get-DslText $n.Text
$styleName = Get-StyleName $n.FormatIdx
if ($styleName -ne "default") { $dslNote["style"] = $styleName }
if (-not $n.AutoSize) { $dslNote["autoSize"] = $false }
$dslNote["box"] = $n.Box
if ($n.AnchorRow -ne 1 -or $n.AnchorCol -ne 1) {
$dslNote["anchor"] = [ordered]@{ row = $n.AnchorRow; col = $n.AnchorCol }
}
$dslCell["note"] = $dslNote
}
$dslCells += $dslCell $dslCells += $dslCell
} }
@@ -1335,7 +1393,14 @@ foreach ($a in $dslAreas) {
$rs = $r.rowStyle $rs = $r.rowStyle
$usedStyles[$(if ($rs -is [System.Collections.IDictionary]) { $rs['style'] } else { $rs })] = $true $usedStyles[$(if ($rs -is [System.Collections.IDictionary]) { $rs['style'] } else { $rs })] = $true
} }
if ($cellList) { foreach ($c in $cellList) { if ($c -isnot [string] -and $c.style) { $usedStyles[$c.style] = $true } } } if ($cellList) {
foreach ($c in $cellList) {
if ($c -isnot [string] -and $c.style) { $usedStyles[$c.style] = $true }
# Четвёртый владелец формата — примечание: его стиль тоже держит ссылку,
# иначе он вырезается как неиспользуемый и ссылка остаётся висячей.
if ($c -isnot [string] -and $c.note -and $c.note['style']) { $usedStyles[$c.note['style']] = $true }
}
}
} }
} }
# Стиль бывает не только у ячейки и строки: колонка — третий владелец формата. Берём стили # Стиль бывает не только у ячейки и строки: колонка — третий владелец формата. Берём стили
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# mxl-decompile v1.22 — Decompile 1C spreadsheet to JSON # mxl-decompile v1.23 — Decompile 1C spreadsheet to JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
@@ -580,6 +580,35 @@ def main():
if v_node is not None: if v_node is not None:
value = (v_node.get(f'{{{XSI_NS}}}type') or '', (v_node.text or '')) value = (v_node.get(f'{{{XSI_NS}}}type') or '', (v_node.text or ''))
# Примечание к ячейке. Якоря не читаем как данные: конец — координаты самой
# ячейки, начало — 1/1 у 1085 примечаний корпуса из 1087; остальное авторское.
note = None
note_node = find(c_content, "d:note")
if note_node is not None:
g = {etree.QName(ch).localname: ch for ch in note_node}
def _txt(name, default=''):
el = g.get(name)
return (el.text or default).strip() if el is not None else default
note_text = OrderedDict()
t_el = g.get('text')
if t_el is not None:
for it in findall(t_el, "v8:item"):
lang = text_of(find(it, "v8:lang")) or ''
note_text[lang] = text_of(find(it, "v8:content")) or ''
note = {
"FormatIdx": int(_txt('formatIndex', '0') or 0),
"Text": note_text,
"AutoSize": _txt('autoSize', 'true') == 'true',
"Box": OrderedDict([
("top", int(_txt('beginRowOffset', '0') or 0)),
("left", int(_txt('beginColumnOffset', '0') or 0)),
("bottom", int(_txt('endRowOffset', '0') or 0)),
("right", int(_txt('endColumnOffset', '0') or 0)),
]),
"AnchorRow": int(_txt('beginRow', '1') or 1),
"AnchorCol": int(_txt('beginColumn', '1') or 1),
}
# Настройки элемента управления — сериализованный base64 у самой ячейки. # Настройки элемента управления — сериализованный base64 у самой ячейки.
# Структуру не разбираем, возим дословно. # Структуру не разбираем, возим дословно.
control = None control = None
@@ -622,6 +651,7 @@ def main():
"Detail": detail, "Detail": detail,
"Value": value, "Value": value,
"Control": control, "Control": control,
"Note": note,
"Text": text, "Text": text,
"HasText": has_text, "HasText": has_text,
}) })
@@ -800,6 +830,15 @@ def main():
if key not in style_keys: if key not in style_keys:
style_keys[key] = fmt style_keys[key] = fmt
format_to_style_key[cell["FormatIdx"]] = key format_to_style_key[cell["FormatIdx"]] = key
# Формат примечания живёт в той же палитре и тоже заслуживает имени: иначе
# оформление подсказки терялось бы при обратной сборке.
if cell.get("Note"):
nfmt = get_format(cell["Note"]["FormatIdx"])
if nfmt:
nkey = get_style_key(nfmt)
if nkey not in style_keys:
style_keys[nkey] = nfmt
format_to_style_key[cell["Note"]["FormatIdx"]] = nkey
def row_style_fmt(fmt): def row_style_fmt(fmt):
"""Оформление строки без её собственных свойств: скрытие уезжает инлайном к height, """Оформление строки без её собственных свойств: скрытие уезжает инлайном к height,
@@ -1069,7 +1108,8 @@ def main():
has_value = bool(cf and cf["Props"].get("containsValue") == "true") has_value = bool(cf and cf["Props"].get("containsValue") == "true")
# Расшифровка сама по себе делает ячейку содержательной: в корпусе 12 653 ячейки # Расшифровка сама по себе делает ячейку содержательной: в корпусе 12 653 ячейки
# несут только её. Без этого такая ячейка уходила в заполнители и терялась. # несут только её. Без этого такая ячейка уходила в заполнители и терялась.
has_content = cell["Param"] or cell["HasText"] or has_value or cell["Detail"] has_content = (cell["Param"] or cell["HasText"] or has_value
or cell["Detail"] or cell["Note"])
has_merge = f"{global_row},{cell['Col']}" in merge_map has_merge = f"{global_row},{cell['Col']}" in merge_map
if has_content or has_merge: if has_content or has_merge:
@@ -1183,6 +1223,20 @@ def main():
if cell["Detail"]: if cell["Detail"]:
dsl_cell["detail"] = cell["Detail"] dsl_cell["detail"] = cell["Detail"]
if cell["Note"]:
n = cell["Note"]
dsl_note = OrderedDict()
dsl_note["text"] = get_dsl_text(n["Text"])
style_name = get_style_name(n["FormatIdx"])
if style_name != "default":
dsl_note["style"] = style_name
if not n["AutoSize"]:
dsl_note["autoSize"] = False
dsl_note["box"] = n["Box"]
if n["AnchorRow"] != 1 or n["AnchorCol"] != 1:
dsl_note["anchor"] = OrderedDict([("row", n["AnchorRow"]), ("col", n["AnchorCol"])])
dsl_cell["note"] = dsl_note
dsl_cells.append(dsl_cell) dsl_cells.append(dsl_cell)
if len(dsl_cells) > 0: if len(dsl_cells) > 0:
@@ -1344,6 +1398,10 @@ def main():
for c in cell_list: for c in cell_list:
if isinstance(c, dict) and "style" in c: if isinstance(c, dict) and "style" in c:
used_styles.add(c["style"]) used_styles.add(c["style"])
# Четвёртый владелец формата — примечание: его стиль тоже держит ссылку,
# иначе он вырезается как неиспользуемый и ссылка остаётся висячей.
if isinstance(c, dict) and isinstance(c.get("note"), dict) and "style" in c["note"]:
used_styles.add(c["note"]["style"])
# Стиль бывает не только у ячейки и строки: колонка — третий владелец формата. Берём # Стиль бывает не только у ячейки и строки: колонка — третий владелец формата. Берём
# стили ИЗ САМИХ РАСКЛАДОК, а не из result: columnSets попадает в результат ПОЗЖЕ этой # стили ИЗ САМИХ РАСКЛАДОК, а не из result: columnSets попадает в результат ПОЗЖЕ этой
# проверки, поэтому стиль, на который ссылается только дополнительная раскладка, # проверки, поэтому стиль, на который ссылается только дополнительная раскладка,
@@ -1,4 +1,4 @@
# mxl-validate v1.4 — Validate 1C spreadsheet # mxl-validate v1.5 — Validate 1C spreadsheet
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Alias('Path')] [Alias('Path')]
@@ -286,6 +286,14 @@ foreach ($ri in $rowNodes) {
Report-Error "Row ${rowIndex}: cell format index $val > format palette size ($formatCount)" Report-Error "Row ${rowIndex}: cell format index $val > format palette size ($formatCount)"
} }
} }
# Примечание — четвёртый владелец формата, и его ссылка тоже бывает битой.
$noteNode = $cell.SelectSingleNode("d:note", $nsMgr)
if ($noteNode) {
$nf = $noteNode.SelectSingleNode("d:formatIndex", $nsMgr)
if ($nf -and [int]$nf.InnerText -gt $formatCount) {
Report-Error "Row ${rowIndex}: note format index $($nf.InnerText) > format palette size ($formatCount)"
}
}
} }
} }
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# mxl-validate v1.4 — Validate 1C spreadsheet document Template.xml # mxl-validate v1.5 — Validate 1C spreadsheet document Template.xml
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates spreadsheet Template.xml: height, palette refs, column/row indices, areas, merges.""" """Validates spreadsheet Template.xml: height, palette refs, column/row indices, areas, merges."""
import sys, os, argparse import sys, os, argparse
@@ -303,6 +303,13 @@ def main():
max_cell_format_ref = val max_cell_format_ref = val
if val > format_count: if val > format_count:
r.error(f'Row {row_index}: cell format index {val} > format palette size ({format_count})') r.error(f'Row {row_index}: cell format index {val} > format palette size ({format_count})')
# Примечание — четвёртый владелец формата, и его ссылка тоже бывает битой.
note = cell.find(f'{{{NS_D}}}note')
if note is not None:
nf = note.find(f'{{{NS_D}}}formatIndex')
if nf is not None and nf.text and int(nf.text) > format_count:
r.error(f'Row {row_index}: note format index {nf.text}'
f' > format palette size ({format_count})')
row_index += 1 row_index += 1
+37
View File
@@ -601,6 +601,43 @@ current-config не объявляет, поэтому вынести объяв
обратные, ячейки продолжают ссылаться на записи со старыми ширинами. Из итогового XML это обратные, ячейки продолжают ссылаться на записи со старыми ширинами. Из итогового XML это
значение не выводится — воспроизводить его не нужно. значение не выводится — воспроизводить его не нужно.
## Примечание к ячейке
```xml
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text><v8:item><v8:lang>ru</v8:lang><v8:content>тест</v8:content></v8:item></text>
<beginRow>1</beginRow> <beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow> <endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn> <beginColumnOffset>21</beginColumnOffset>
<endColumn>0</endColumn> <endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
```
Конструкция редкая (188 макетов, 1 087 примечаний), но структура жёсткая: все четырнадцать тегов
присутствуют у всех 1 087, порядок один и тот же, опциональных нет. Информации при этом меньше,
чем тегов:
| Тег | Наблюдение |
|---|---|
| `drawingType`, `pictureSize`, `id` | константы: `Comment`, `Stretch`, `0` |
| `beginRow`, `beginColumn` | всегда `1`/`1` — 1085 и 1086 из 1087; три исключения в одном макете |
| `endRow`, `endColumn` | **координаты самой ячейки** — 1087 из 1087 |
| четыре смещения | авторские: сдвиг окошка меняет `begin*Offset`, растяжение — `end*Offset` |
| `autoSize` | `true` у 1036 из 1087 |
`autoSize` описывает не наличие геометрии, а пересчёт размера: при `true` окошко всё равно несёт
координаты, и они осмысленны — 306 различных пар `(endRowOffset, endColumnOffset)` против
12 различных пар положения.
Формат примечания — обычная запись палитры, на корпусе их всего 7 различных: 926 — стиль
подсказки (`verticalAlignment: Top` + `style:ToolTipTextColor` + `style:ToolTipBackColor`),
105 — он же с заливкой `#FFFAD9`.
## Ресурсы картинок ## Ресурсы картинок
```xml ```xml
+31 -1
View File
@@ -188,6 +188,7 @@
| `controlType` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` | | `controlType` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` |
| `value` | нет | — | Значение в поле ввода. Только вместе с `valueType` | | `value` | нет | — | Значение в поле ввода. Только вместе с `valueType` |
| `control` | нет | — | Настройки элемента управления в записи платформы (base64). Раундтрип, не для ручного авторинга | | `control` | нет | — | Настройки элемента управления в записи платформы (base64). Раундтрип, не для ручного авторинга |
| `note` | нет | — | Примечание к ячейке (см. ниже) |
### Содержимое ячейки ### Содержимое ячейки
@@ -270,6 +271,35 @@
включая `Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — включая `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` — оформление строки ## `rowStyle` — оформление строки
Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком. Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком.
@@ -285,7 +315,7 @@ round-trip** (`/mxl-decompile` → `/mxl-compile`): в JSON оно не попа
XML не возвращается. XML не возвращается.
- объединения, не привязанные к ячейке (по всей высоте или ширине документа); - объединения, не привязанные к ячейке (по всей высоте или ширине документа);
- рисунки и картинки, в том числе штрихкоды, и примечания к ячейкам; - рисунки и картинки, в том числе штрихкоды;
- группировки строк и колонок; - группировки строк и колонок;
- колонтитулы, параметры печати, область печати. - колонтитулы, параметры печати, область печати.
@@ -0,0 +1,23 @@
{
"name": "Примечания к ячейкам: короткая и полная формы",
"input": {
"columns": 4,
"textLanguages": ["ru"],
"styles": { "жёлтая-подсказка": { "verticalAlignment": "Top", "backColor": "#FFFAD9" } },
"areas": [
{
"name": "Примечания",
"rows": [
{ "cells": [
{ "col": 1, "text": "Итого", "note": "Сумма без НДС" },
{ "col": 2, "text": "Курс", "note": { "ru": "на дату документа", "en": "as of the document date" } },
{ "col": 3, "param": "Скидка", "note": { "text": "не более 20%", "style": "жёлтая-подсказка" } },
{ "col": 4, "note": { "text": "сдвинуто и растянуто", "autoSize": false,
"box": { "top": 58, "left": -175, "bottom": 362, "right": 478 } } }
]}
]
}
]
},
"params": { "outputPath": "Template.xml" }
}
@@ -0,0 +1,154 @@
<?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>3</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>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>тест</v8:content>
</v8:item>
<v8:item>
<v8:lang>en</v8:lang>
<v8:content>test</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-19</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>50</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>19</beginColumnOffset>
<endColumn>0</endColumn>
<endColumnOffset>82</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Изменен размер</v8:content>
</v8:item>
</tl>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Изменен размер</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-19</beginRowOffset>
<endRow>1</endRow>
<endRowOffset>362</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>19</beginColumnOffset>
<endColumn>0</endColumn>
<endColumnOffset>478</endColumnOffset>
<autoSize>false</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<i>2</i>
<c>
<f>0</f>
<tl/>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>2</index>
<row>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сдивнут</v8:content>
</v8:item>
</tl>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сдвинут</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>58</beginRowOffset>
<endRow>2</endRow>
<endRowOffset>50</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>-175</beginColumnOffset>
<endColumn>0</endColumn>
<endColumnOffset>144</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>2</defaultFormatIndex>
<height>3</height>
<vgRows>3</vgRows>
<format>
<verticalAlignment>Top</verticalAlignment>
<textColor>style:ToolTipTextColor</textColor>
<backColor>style:ToolTipBackColor</backColor>
</format>
<format>
<width>72</width>
</format>
</document>
@@ -0,0 +1,25 @@
{
"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,170 @@
<?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>4</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>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сумма без НДС</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>21</beginColumnOffset>
<endColumn>0</endColumn>
<endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Курс</v8:content>
</v8:item>
</tl>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>на дату документа</v8:content>
</v8:item>
<v8:item>
<v8:lang>en</v8:lang>
<v8:content>as of the document date</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>21</beginColumnOffset>
<endColumn>1</endColumn>
<endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<c>
<f>2</f>
<parameter>Скидка</parameter>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>3</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>не более 20%</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>21</beginColumnOffset>
<endColumn>2</endColumn>
<endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<c>
<f>0</f>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>сдвинуто и растянуто</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>58</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>362</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>-175</beginColumnOffset>
<endColumn>3</endColumn>
<endColumnOffset>478</endColumnOffset>
<autoSize>false</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>4</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Примечания</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<format>
<verticalAlignment>Top</verticalAlignment>
<textColor>style:ToolTipTextColor</textColor>
<backColor>style:ToolTipBackColor</backColor>
</format>
<format>
<fillType>Parameter</fillType>
</format>
<format>
<verticalAlignment>Top</verticalAlignment>
<backColor>#FFFAD9</backColor>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,36 @@
{
"name": "Roundtrip — примечания к ячейкам",
"preRun": [
{
"script": "mxl-compile/scripts/mxl-compile",
"input": {
"columns": 4,
"textLanguages": ["ru"],
"styles": { "жёлтая-подсказка": { "verticalAlignment": "Top", "backColor": "#FFFAD9" } },
"areas": [
{
"name": "Примечания",
"rows": [
{ "cells": [
{ "col": 1, "text": "Итого", "note": "Сумма без НДС" },
{ "col": 2, "text": "Курс", "note": { "ru": "на дату", "en": "as of date" } },
{ "col": 3, "param": "Скидка", "note": { "text": "не более 20%", "style": "жёлтая-подсказка" } },
{ "col": 4, "note": { "text": "сдвинуто", "autoSize": false,
"box": { "top": 58, "left": -175, "bottom": 362, "right": 478 },
"anchor": { "row": 4, "col": 1 } } }
]}
]
}
]
},
"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,170 @@
<?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>4</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>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сумма без НДС</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>21</beginColumnOffset>
<endColumn>0</endColumn>
<endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<c>
<f>0</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Курс</v8:content>
</v8:item>
</tl>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>на дату</v8:content>
</v8:item>
<v8:item>
<v8:lang>en</v8:lang>
<v8:content>as of date</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>21</beginColumnOffset>
<endColumn>1</endColumn>
<endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<c>
<f>2</f>
<parameter>Скидка</parameter>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>3</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>не более 20%</v8:content>
</v8:item>
</text>
<beginRow>1</beginRow>
<beginRowOffset>-21</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>51</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>21</beginColumnOffset>
<endColumn>2</endColumn>
<endColumnOffset>408</endColumnOffset>
<autoSize>true</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
<c>
<c>
<f>0</f>
<note>
<drawingType>Comment</drawingType>
<id>0</id>
<formatIndex>1</formatIndex>
<text>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>сдвинуто</v8:content>
</v8:item>
</text>
<beginRow>4</beginRow>
<beginRowOffset>58</beginRowOffset>
<endRow>0</endRow>
<endRowOffset>362</endRowOffset>
<beginColumn>1</beginColumn>
<beginColumnOffset>-175</beginColumnOffset>
<endColumn>3</endColumn>
<endColumnOffset>478</endColumnOffset>
<autoSize>false</autoSize>
<pictureSize>Stretch</pictureSize>
</note>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>4</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Примечания</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<format>
<verticalAlignment>Top</verticalAlignment>
<textColor>style:ToolTipTextColor</textColor>
<backColor>style:ToolTipBackColor</backColor>
</format>
<format>
<fillType>Parameter</fillType>
</format>
<format>
<verticalAlignment>Top</verticalAlignment>
<backColor>#FFFAD9</backColor>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,19 @@
{
"columns": 4,
"defaultWidth": 10,
"fonts": {},
"styles": { "vtop-bg": { "verticalAlignment": "Top", "backColor": "#FFFAD9" } },
"areas": [
{
"name": "Примечания",
"rows": [
[
{ "text": "Итого", "note": { "text": "Сумма без НДС", "box": { "top": -21, "left": 21, "bottom": 51, "right": 408 } } },
{ "text": "Курс", "note": { "text": { "ru": "на дату", "en": "as of date" }, "box": { "top": -21, "left": 21, "bottom": 51, "right": 408 } } },
{ "param": "Скидка", "note": { "text": "не более 20%", "style": "vtop-bg", "box": { "top": -21, "left": 21, "bottom": 51, "right": 408 } } },
{ "note": { "text": "сдвинуто", "autoSize": false, "box": { "top": 58, "left": -175, "bottom": 362, "right": 478 }, "anchor": { "row": 4, "col": 1 } } }
]
]
}
]
}