mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-19 17:50:23 +03:00
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:
co-authored by
Claude Opus 5
parent
faa8ab7659
commit
4005f36cce
@@ -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
|
||||
param(
|
||||
[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 }
|
||||
}
|
||||
|
||||
# Примечание к ячейке. Якоря не читаем как данные: конец — координаты самой
|
||||
# ячейки, начало — 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 у самой ячейки.
|
||||
# Структуру не разбираем, возим дословно.
|
||||
$control = $null
|
||||
@@ -460,6 +493,7 @@ foreach ($riNode in $root.SelectNodes("d:rowsItem", $ns)) {
|
||||
Detail = $detail
|
||||
Value = $value
|
||||
Control = $control
|
||||
Note = $note
|
||||
Text = $text
|
||||
HasText = $hasText
|
||||
}
|
||||
@@ -646,6 +680,16 @@ foreach ($r in ($rowData.Keys | Sort-Object { [int]$_ } | ForEach-Object { $rowD
|
||||
$key = Get-StyleKey $fmt
|
||||
if (-not $styleKeys.Contains($key)) { $styleKeys[$key] = $fmt }
|
||||
$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')
|
||||
# Расшифровка сама по себе делает ячейку содержательной: в корпусе 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)")
|
||||
|
||||
if ($hasContent -or $hasMerge) {
|
||||
@@ -1193,6 +1237,20 @@ foreach ($area in $blocks) {
|
||||
# с параметром, две трети расшифровок терялись молча.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1335,7 +1393,14 @@ foreach ($a in $dslAreas) {
|
||||
$rs = $r.rowStyle
|
||||
$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
|
||||
# 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
|
||||
|
||||
import argparse
|
||||
@@ -580,6 +580,35 @@ def main():
|
||||
if v_node is not None:
|
||||
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 у самой ячейки.
|
||||
# Структуру не разбираем, возим дословно.
|
||||
control = None
|
||||
@@ -622,6 +651,7 @@ def main():
|
||||
"Detail": detail,
|
||||
"Value": value,
|
||||
"Control": control,
|
||||
"Note": note,
|
||||
"Text": text,
|
||||
"HasText": has_text,
|
||||
})
|
||||
@@ -800,6 +830,15 @@ def main():
|
||||
if key not in style_keys:
|
||||
style_keys[key] = fmt
|
||||
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):
|
||||
"""Оформление строки без её собственных свойств: скрытие уезжает инлайном к height,
|
||||
@@ -1069,7 +1108,8 @@ def main():
|
||||
has_value = bool(cf and cf["Props"].get("containsValue") == "true")
|
||||
# Расшифровка сама по себе делает ячейку содержательной: в корпусе 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
|
||||
|
||||
if has_content or has_merge:
|
||||
@@ -1183,6 +1223,20 @@ def main():
|
||||
if 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)
|
||||
|
||||
if len(dsl_cells) > 0:
|
||||
@@ -1344,6 +1398,10 @@ def main():
|
||||
for c in cell_list:
|
||||
if isinstance(c, dict) and "style" in c:
|
||||
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 попадает в результат ПОЗЖЕ этой
|
||||
# проверки, поэтому стиль, на который ссылается только дополнительная раскладка,
|
||||
|
||||
Reference in New Issue
Block a user