diff --git a/.claude/skills/mxl-compile/SKILL.md b/.claude/skills/mxl-compile/SKILL.md index 7e61a227..d0f9cb80 100644 --- a/.claude/skills/mxl-compile/SKILL.md +++ b/.claude/skills/mxl-compile/SKILL.md @@ -67,7 +67,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J header: { left, center, right, font, verticalAlignment, show, startPage }, footer: { … }, printSettings: { pageOrientation, topMargin, …, fitToPage, firstPageNumber }, columnSets: { name: { columns, columnWidths, columnStyles } }, - pictures: { name: { ref } | { data, transparent, transparentPixel } | {} }, + pictures: { name: { ref } | { data } | {} + transparent: false | { x, y } }, drawings: [{ type, begin: { row, col, dy, dx }, end: { … }, picture, pictureSize, text, name, detail, style, line, sides, id, zOrder }] } diff --git a/.claude/skills/mxl-compile/reference/dsl-spec.md b/.claude/skills/mxl-compile/reference/dsl-spec.md index dab01931..442a43bc 100644 --- a/.claude/skills/mxl-compile/reference/dsl-spec.md +++ b/.claude/skills/mxl-compile/reference/dsl-spec.md @@ -386,7 +386,7 @@ ```json "pictures": { "знак": { "ref": "v8ui:Стоп48" }, - "логотип": { "data": "iVBORw0KGgo...", "transparent": false } + "логотип": { "data": "iVBORw0KGgo...", "transparent": { "x": 24, "y": 29 } } }, "drawings": [ { "type": "Picture", "picture": "логотип", "name": "Логотип", @@ -422,10 +422,9 @@ Запись в `pictures` — либо ссылка (`ref`), либо сами данные в base64 (`data`). Ссылкой задаются и предопределённая картинка платформы, и общая картинка конфигурации — пишутся они одинаково, префиксом `v8ui:`. Пустая запись `{}` — картинка не задана, такое в макетах встречается. -С данными сочетаются `transparent` и `transparentPixel` — два способа записать прозрачность, -исключающие друг друга: `transparent: false` — прозрачного фона нет, -`transparentPixel: { "x": …, "y": … }` — прозрачным считается цвет пикселя с этими координатами -(Конфигуратор по флажку «прозрачный фон» берёт правый нижний пиксель картинки). +Прозрачность задаётся ключом `transparent` в одной из двух форм: `false` — прозрачного фона +нет; `{ "x": …, "y": … }` — прозрачным считается цвет пикселя с этими координатами внутри +картинки (по флажку «прозрачный фон» Конфигуратор берёт её правый нижний пиксель). Одну запись `pictures` могут использовать несколько рисунков — данные в макете не дублируются. diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 index 5d19937b..8f3d45b5 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 @@ -1,4 +1,4 @@ -# mxl-compile v1.49 — Compile 1C spreadsheet from JSON +# mxl-compile v1.50 — Compile 1C spreadsheet from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -1307,7 +1307,7 @@ function Set-CellProp { function Test-CellObject { param($el) $cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template', - 'valueType', 'controlType', 'value', 'control', 'note') + 'valueType', 'controlType', 'value', 'control', 'note', 'pictureParameter') foreach ($p in $el.PSObject.Properties) { if ($cellKeys -contains $p.Name) { return $true } } @@ -1603,16 +1603,23 @@ if ($def.pictures) { $entry = @{ Ref = ''; Data = ''; Transparent = ''; PixelX = ''; PixelY = '' } if ($pr.Value.ref) { $entry.Ref = "$($pr.Value.ref)" } if ($null -ne $pr.Value.data) { $entry.Data = "$($pr.Value.data)" } - if ($null -ne $pr.Value.transparent) { - $entry.Transparent = if ($pr.Value.transparent -eq $true -or "$($pr.Value.transparent)" -eq 'true') { 'true' } else { 'false' } + # Прозрачность — одна сущность диалога, записанная двумя способами: выключенную + # платформа хранит атрибутом, включённую — координатами пикселя, чей цвет прозрачен. + # Поэтому и ключ один, а форма значения выбирает способ. + $tr = $pr.Value.transparent + if ($null -ne $tr) { + if ($tr -is [bool] -or "$tr" -ceq 'true' -or "$tr" -ceq 'false') { + $entry.Transparent = if ($tr -eq $true -or "$tr" -ceq 'true') { 'true' } else { 'false' } + } elseif ($null -ne $tr.x -and $null -ne $tr.y) { + $entry.PixelX = "$([int]$tr.x)" + $entry.PixelY = "$([int]$tr.y)" + } else { + [Console]::Error.WriteLine("pictures[$($pr.Name)]: 'transparent' is either false/true or { x, y }") + exit 1 + } } - if ($null -ne $pr.Value.transparentPixel) { - $entry.PixelX = "$([int]$pr.Value.transparentPixel.x)" - $entry.PixelY = "$([int]$pr.Value.transparentPixel.y)" - } - if (-not $entry.Ref -and -not $entry.Data -and - ($entry.Transparent -or $entry.PixelX)) { - [Console]::Error.WriteLine("pictures[$($pr.Name)]: 'transparent' and 'transparentPixel' require 'ref' or 'data'") + if (-not $entry.Ref -and -not $entry.Data -and ($entry.Transparent -or $entry.PixelX)) { + [Console]::Error.WriteLine("pictures[$($pr.Name)]: 'transparent' requires 'ref' or 'data'") exit 1 } $pictureEntries += $entry @@ -1976,8 +1983,9 @@ foreach ($area in $def.areas) { $cellInfo = @{ Col = $colStart - 1 # 0-based FormatIdx = $fmtIdx - Param = $cell.param - Detail = $cell.detail + Param = $cell.param + Detail = $cell.detail + PictureParam = $cell.pictureParameter Text = $cell.text Template = $cell.template Value = $(if ($vp.Count -gt 0) { Get-CellValue $cell "$($vp['valueType'])" "area `"$areaName`", row $($localRow + 1)" } else { $null }) @@ -2118,6 +2126,13 @@ foreach ($area in $def.areas) { X "`t`t`t`t`t$($cellInfo.Detail)" } + # Третий параметр ячейки — имя параметра, которым подставляют картинку. Сама + # картинка при этом задаётся оформлением (picIndex), а параметр — тут, последним + # из параметров ячейки (21 ячейка корпуса, порядок везде такой). + if ($cellInfo.PictureParam) { + X "`t`t`t`t`t$(Esc-XmlText $cellInfo.PictureParam)" + } + # Якорь конца примечания — координаты самой ячейки, поэтому его не задают: # он выводится здесь, при эмиссии. if ($null -ne $cellInfo.Note) { diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.py b/.claude/skills/mxl-compile/scripts/mxl-compile.py index 8f0b8973..6d3ec4a4 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.py +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-compile v1.49 — Compile 1C spreadsheet from JSON +# mxl-compile v1.50 — Compile 1C spreadsheet from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import hashlib @@ -1299,7 +1299,7 @@ def main(): ключей есть ключ схемы ячейки, во втором ключи — идентификаторы языков. Пересечений нет: в корпусе это ru, en, ru1, Русский.""" cell_keys = ('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template', - 'valueType', 'controlType', 'value', 'control') + 'valueType', 'controlType', 'value', 'control', 'note', 'pictureParameter') return any(k in el for k in cell_keys) def expand_shorthand_row(row, area_name, row_idx, open_by_col, max_cols): @@ -1560,14 +1560,21 @@ def main(): entry['Ref'] = str(pic_def['ref']) if pic_def.get('data') is not None: entry['Data'] = str(pic_def['data']) - if pic_def.get('transparent') is not None: - entry['Transparent'] = 'true' if (pic_def['transparent'] is True - or str(pic_def['transparent']).lower() == 'true') else 'false' - if pic_def.get('transparentPixel') is not None: - entry['PixelX'] = str(int(pic_def['transparentPixel'].get('x', 0))) - entry['PixelY'] = str(int(pic_def['transparentPixel'].get('y', 0))) + # Прозрачность — одна сущность диалога, записанная двумя способами: выключенную + # платформа хранит атрибутом, включённую — координатами пикселя, чей цвет прозрачен. + # Поэтому и ключ один, а форма значения выбирает способ. + tr = pic_def.get('transparent') + if tr is not None: + if isinstance(tr, bool) or str(tr).lower() in ('true', 'false'): + entry['Transparent'] = 'true' if (tr is True or str(tr).lower() == 'true') else 'false' + elif isinstance(tr, dict) and 'x' in tr and 'y' in tr: + entry['PixelX'] = str(int(tr['x'])) + entry['PixelY'] = str(int(tr['y'])) + else: + print(f"pictures[{pic_name}]: 'transparent' is either false/true or {{ x, y }}", file=sys.stderr) + sys.exit(1) if not entry['Ref'] and not entry['Data'] and (entry['Transparent'] or entry['PixelX']): - print(f"pictures[{pic_name}]: 'transparent' and 'transparentPixel' require 'ref' or 'data'", file=sys.stderr) + print(f"pictures[{pic_name}]: 'transparent' requires 'ref' or 'data'", file=sys.stderr) sys.exit(1) picture_entries.append(entry) picture_names[pic_name] = len(picture_entries) @@ -1885,6 +1892,7 @@ def main(): 'FormatIdx': fmt_idx, 'Param': cell.get('param'), 'Detail': cell.get('detail'), + 'PictureParam': cell.get('pictureParameter'), 'Text': cell.get('text'), 'Template': cell.get('template'), 'Value': (cell_value(cell, vp.get('valueType', ''), @@ -2015,6 +2023,13 @@ def main(): if cell_info['Detail']: lines.append(f'\t\t\t\t\t{cell_info["Detail"]}') + # Третий параметр ячейки — имя параметра, которым подставляют картинку. Сама + # картинка при этом задаётся оформлением (picIndex), а параметр — тут, последним + # из параметров ячейки (21 ячейка корпуса, порядок везде такой). + if cell_info.get('PictureParam'): + lines.append('\t\t\t\t\t' + f'{esc_xml_text(cell_info["PictureParam"])}') + # Якорь конца примечания — координаты самой ячейки, поэтому его не задают: # он выводится здесь, при эмиссии. if cell_info.get('Note') is not None: diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 index f67f4e70..9775c3f6 100644 --- a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 +++ b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 @@ -1,4 +1,4 @@ -# mxl-decompile v1.28 — Decompile 1C spreadsheet to JSON +# mxl-decompile v1.29 — Decompile 1C spreadsheet to JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -290,7 +290,7 @@ foreach ($picNode in $root.SelectNodes("d:picture", $ns)) { $entry["transparent"] = ($inner.GetAttribute('t') -ceq 'true') } if ($entry.Count -gt 0 -and $inner.HasAttribute('tx')) { - $entry["transparentPixel"] = [ordered]@{ + $entry["transparent"] = [ordered]@{ x = [int]$inner.GetAttribute('tx') y = [int]$inner.GetAttribute('ty') } @@ -603,6 +603,12 @@ foreach ($riNode in $root.SelectNodes("d:rowsItem", $ns)) { $dNode = $cContent.SelectSingleNode("d:detailParameter", $ns) if ($dNode) { $detail = $dNode.InnerText } + # Имя параметра, которым подставляют картинку. Сама картинка сидит в + # оформлении (picIndex), поэтому этот тег живёт отдельно от неё. + $picParam = $null + $ppNode = $cContent.SelectSingleNode("d:pictureParameter", $ns) + if ($ppNode) { $picParam = $ppNode.InnerText } + # Значение ячейки-поля ввода. Тип значения свой, из объявленного не выводится, # поэтому читаем и его. $value = $null @@ -685,6 +691,7 @@ foreach ($riNode in $root.SelectNodes("d:rowsItem", $ns)) { FormatIdx = $cellFmtIdx Param = $param Detail = $detail + PictureParam = $picParam Value = $value Control = $control Note = $note @@ -1337,7 +1344,7 @@ foreach ($area in $blocks) { $hasValue = ($cf -and $cf.Props['containsValue'] -ceq 'true') # Расшифровка сама по себе делает ячейку содержательной: в корпусе 12 653 ячейки # несут только её. Без этого такая ячейка уходила в заполнители и терялась. - $hasContent = $cell.Param -or $cell.HasText -or $hasValue -or $cell.Detail -or $cell.Note + $hasContent = $cell.Param -or $cell.HasText -or $hasValue -or $cell.Detail -or $cell.Note -or $cell.PictureParam $hasMerge = $mergeMap.ContainsKey("$globalRow,$($cell.Col)") if ($hasContent -or $hasMerge) { @@ -1455,6 +1462,7 @@ foreach ($area in $blocks) { # несут её без параметра против 8 582 с ним. Пока она читалась только вместе # с параметром, две трети расшифровок терялись молча. if ($cell.Detail) { $dslCell["detail"] = $cell.Detail } + if ($cell.PictureParam) { $dslCell["pictureParameter"] = $cell.PictureParam } if ($cell.Note) { $n = $cell.Note diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py index ad680ba4..c9819005 100644 --- a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py +++ b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-decompile v1.28 — Decompile 1C spreadsheet to JSON +# mxl-decompile v1.29 — Decompile 1C spreadsheet to JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -451,8 +451,8 @@ def main(): if entry and inner.get("t") is not None: entry["transparent"] = inner.get("t") == "true" if entry and inner.get("tx") is not None: - entry["transparentPixel"] = OrderedDict([("x", int(inner.get("tx"))), - ("y", int(inner.get("ty") or 0))]) + entry["transparent"] = OrderedDict([("x", int(inner.get("tx"))), + ("y", int(inner.get("ty") or 0))]) pictures_out[name] = entry picture_key[pic_i] = name @@ -738,6 +738,13 @@ def main(): if d_node is not None and d_node.text: detail = d_node.text + # Имя параметра, которым подставляют картинку. Сама картинка сидит в + # оформлении (picIndex), поэтому этот тег живёт отдельно от неё. + pic_param = None + pp_node = find(c_content, "d:pictureParameter") + if pp_node is not None and pp_node.text: + pic_param = pp_node.text + # Значение ячейки-поля ввода. Тип значения свой, из объявленного не выводится, # поэтому читаем и его: пустое значение сворачивается в "", остальные едут как есть. value = None @@ -814,6 +821,7 @@ def main(): "FormatIdx": cell_fmt_idx, "Param": param, "Detail": detail, + "PictureParam": pic_param, "Value": value, "Control": control, "Note": note, @@ -1296,7 +1304,7 @@ def main(): # Расшифровка сама по себе делает ячейку содержательной: в корпусе 12 653 ячейки # несут только её. Без этого такая ячейка уходила в заполнители и терялась. has_content = (cell["Param"] or cell["HasText"] or has_value - or cell["Detail"] or cell["Note"]) + or cell["Detail"] or cell["Note"] or cell["PictureParam"]) has_merge = f"{global_row},{cell['Col']}" in merge_map if has_content or has_merge: @@ -1409,6 +1417,8 @@ def main(): # с параметром, две трети расшифровок терялись молча. if cell["Detail"]: dsl_cell["detail"] = cell["Detail"] + if cell["PictureParam"]: + dsl_cell["pictureParameter"] = cell["PictureParam"] if cell["Note"]: n = cell["Note"] diff --git a/docs/mxl-dsl-spec.md b/docs/mxl-dsl-spec.md index 4a4aeb11..f1738cee 100644 --- a/docs/mxl-dsl-spec.md +++ b/docs/mxl-dsl-spec.md @@ -394,7 +394,7 @@ ```json "pictures": { "знак": { "ref": "v8ui:Стоп48" }, - "логотип": { "data": "iVBORw0KGgo...", "transparent": false } + "логотип": { "data": "iVBORw0KGgo...", "transparent": { "x": 24, "y": 29 } } }, "drawings": [ { "type": "Picture", "picture": "логотип", "name": "Логотип", @@ -430,10 +430,9 @@ Запись в `pictures` — либо ссылка (`ref`), либо сами данные в base64 (`data`). Ссылкой задаются и предопределённая картинка платформы, и общая картинка конфигурации — пишутся они одинаково, префиксом `v8ui:`. Пустая запись `{}` — картинка не задана, такое в макетах встречается. -С данными сочетаются `transparent` и `transparentPixel` — два способа записать прозрачность, -исключающие друг друга: `transparent: false` — прозрачного фона нет, -`transparentPixel: { "x": …, "y": … }` — прозрачным считается цвет пикселя с этими координатами -(Конфигуратор по флажку «прозрачный фон» берёт правый нижний пиксель картинки). +Прозрачность задаётся ключом `transparent` в одной из двух форм: `false` — прозрачного фона +нет; `{ "x": …, "y": … }` — прозрачным считается цвет пикселя с этими координатами внутри +картинки (по флажку «прозрачный фон» Конфигуратор берёт её правый нижний пиксель). Одну запись `pictures` могут использовать несколько рисунков — данные в макете не дублируются. diff --git a/tests/skills/cases/mxl-compile/cell-picture-parameter.json b/tests/skills/cases/mxl-compile/cell-picture-parameter.json new file mode 100644 index 00000000..d71e921b --- /dev/null +++ b/tests/skills/cases/mxl-compile/cell-picture-parameter.json @@ -0,0 +1,19 @@ +{ + "name": "Картинка в ячейке, подставляемая параметром", + "input": { + "columns": 3, + "pictures": { "статус": { "ref": "v8ui:Стоп48" } }, + "styles": { + "значок": { "picIndex": 1, "picHorizontalAlignment": "Center", "picVerticalAlignment": "Center", "textPosition": "Bottom" } + }, + "areas": [{ + "rows": [ + { "cells": [ + { "col": 1, "style": "значок", "text": "удалить", "pictureParameter": "Удалить" }, + { "col": 2, "param": "ПорядковыйНомер", "detail": "Строка", "pictureParameter": "Пиктограмма" } + ]} + ] + }] + }, + "params": { "outputPath": "Template.xml" } +} diff --git a/tests/skills/cases/mxl-compile/error-picture-transparent-without-data.json b/tests/skills/cases/mxl-compile/error-picture-transparent-without-data.json index b3524da5..a66ffe7b 100644 --- a/tests/skills/cases/mxl-compile/error-picture-transparent-without-data.json +++ b/tests/skills/cases/mxl-compile/error-picture-transparent-without-data.json @@ -6,5 +6,5 @@ "areas": [{ "rows": [["Бланк"]] }] }, "params": { "outputPath": "Template.xml" }, - "expectError": "pictures[пусто]: 'transparent' and 'transparentPixel' require 'ref' or 'data'" + "expectError": "pictures[пусто]: 'transparent' requires 'ref' or 'data'" } diff --git a/tests/skills/cases/mxl-compile/pictures-empty-and-transparent.json b/tests/skills/cases/mxl-compile/pictures-empty-and-transparent.json index b5c9a387..d734354d 100644 --- a/tests/skills/cases/mxl-compile/pictures-empty-and-transparent.json +++ b/tests/skills/cases/mxl-compile/pictures-empty-and-transparent.json @@ -7,7 +7,7 @@ "значок": { "ref": "v8ui:Стоп48", "transparent": false }, "точка": { "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/q842iQAAAABJRU5ErkJggg==", - "transparentPixel": { "x": 3, "y": 5 } + "transparent": { "x": 3, "y": 5 } } }, "areas": [{ "rows": [["Бланк"]] }], diff --git a/tests/skills/cases/mxl-compile/snapshots/cell-picture-parameter/Template.xml b/tests/skills/cases/mxl-compile/snapshots/cell-picture-parameter/Template.xml new file mode 100644 index 00000000..0ec34cd5 --- /dev/null +++ b/tests/skills/cases/mxl-compile/snapshots/cell-picture-parameter/Template.xml @@ -0,0 +1,60 @@ + + + + ru + ru + + ru + Русский + Русский + + + + 3 + + + 0 + + + + 1 + + + ru + удалить + + + Удалить + + + + + 2 + ПорядковыйНомер + Строка + Пиктограмма + + + + + true + 3 + 1 + 1 + + 1 + Center + Center + Bottom + + + Parameter + + + 10 + + + 0 + + + \ No newline at end of file