feat(mxl-compile,mxl-decompile): columnStyles — оформление колонки

У ячейки есть style, у строки rowStyle, у колонки не было ничего — при том что
колонка ссылается в ту же палитру <format> и несёт те же свойства. В корпусе
ERP колонки используют шрифт (3006 форматов), выравнивание, рамки, скрытие и
прочее; всё это терялось при раундтрипе.

Колонка получает такой же именованный стиль: columnStyles рядом с columnWidths,
ключи той же грамматики диапазонов ("1", "2-8", "5,7,9"), значение — имя из
styles. Внутри columnSets тот же ключ. Формат колонки собирается из ширины и
свойств стиля: запись в палитре одна.

Шрифт по умолчанию колонке не навязываем — формат колонки без оформления это
ровно <width>, как пишет платформа.

Попутно два дефекта декомпилятора: стиль колонки не учитывался при отсечении
неиспользуемых стилей и пропадал целиком, а набор из одних неприметных свойств
(отступ, защита) получал зарезервированное имя default и тоже терялся.

На пилоте потери в категории colset[].col[].formatIndex упали с 86 до 39.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-11 16:57:52 +03:00
co-authored by Claude Opus 5
parent c9e490bd25
commit 4e389d0ef1
6 changed files with 350 additions and 20 deletions
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-compile v1.29 — Compile 1C spreadsheet from JSON
# mxl-compile v1.30 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import hashlib
@@ -595,6 +595,18 @@ def main():
col_width_map = build_col_width_map(defn.get('columnWidths'))
# Стиль колонки — тот же именованный стиль, что у ячейки и строки: колонка третий
# владелец формата, и своих свойств у неё нет. Ключи те же, что у columnWidths.
def build_col_style_map(styles):
out = {}
if styles:
for prop_name, prop_value in styles.items():
for c in parse_column_spec(prop_name):
out[c] = str(prop_value)
return out
col_style_map = build_col_style_map(defn.get('columnStyles'))
# Колоночные раскладки: документные columns/columnWidths — раскладка по умолчанию (в XML
# элемент <columns> БЕЗ <id>, он всегда идёт первым). Дополнительные объявляются в
# columnSets, ключ — идентификатор, на него ссылается область ключом columnSet.
@@ -616,7 +628,8 @@ def main():
h = b.hex()
return f'{h[0:8]}-{h[8:12]}-{h[12:16]}-{h[16:20]}-{h[20:32]}'
column_layouts = [{'Id': None, 'Name': None, 'Size': total_columns, 'Widths': col_width_map}]
column_layouts = [{'Id': None, 'Name': None, 'Size': total_columns,
'Widths': col_width_map, 'Styles': col_style_map}]
for set_name, cs in (defn.get('columnSets') or {}).items():
size = int(cs['columns']) if cs.get('columns') is not None else total_columns
column_layouts.append({
@@ -624,6 +637,7 @@ def main():
'Name': set_name,
'Size': size,
'Widths': build_col_width_map(cs.get('columnWidths')),
'Styles': build_col_style_map(cs.get('columnStyles')),
})
# --- 5. Style resolver ---
@@ -651,10 +665,10 @@ def main():
sys.exit(1)
return {'Style': canon, 'Width': width, 'Gap': gap}
def resolve_style(style_name, fill_type):
def resolve_style(style_name, fill_type, no_default_font=False):
# Набор свойств формата — «тег платформы → значение», только заданные. Порядок вставки
# роли не играет: и ключ дедупликации, и эмиссия идут по каноническому порядку тегов.
props = {'font': font_map.get('default', 0)}
props = {} if no_default_font else {'font': font_map.get('default', 0)}
if style_name and defn.get('styles'):
style = defn['styles'].get(style_name)
@@ -759,12 +773,19 @@ def main():
# 6a. Default width format
default_format_index = register_format({'width': default_width})
# 6b. Column width formats — по одной карте на каждую колоночную раскладку
# 6b. Column formats — по одной карте на каждую колоночную раскладку.
# У колонки бывает и ширина, и оформление: формат один, свойства складываются.
for layout in column_layouts:
fmap = {} # 1-based col -> format index
for col in sorted(layout['Widths']):
w = layout['Widths'][col]
fmap[int(col)] = register_format({'width': w})
for col in sorted({int(c) for c in list(layout['Widths']) + list(layout['Styles'])}):
props = {}
if col in layout['Styles']:
# Шрифт по умолчанию колонке не навязываем: формат колонки без оформления —
# это ровно <width>, как пишет платформа.
props = resolve_style(layout['Styles'][col], '', no_default_font=True)
if col in layout['Widths']:
props['width'] = layout['Widths'][col]
fmap[col] = register_format(props)
layout['FormatMap'] = fmap
col_format_map = column_layouts[0]['FormatMap']