mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-17 16:50:30 +03:00
feat(mxl-compile,mxl-decompile): позиционный список ячеек в выводе декомпилятора
Компилятор давно принимал короткую форму, а декомпилятор всегда писал самую развёрнутую. Теперь он выбирает самую короткую из применимых, как это делает skd-decompile. Короткая запись стала свойством СПИСКА ЯЧЕЕК, а не строки: cells принимает позиционный список, поэтому строка с height или rowStyle тоже может им пользоваться. Раньше наличие свойств строки лишало её короткой записи — искусственная связка. Если же своих свойств у строки нет, она пишется просто массивом, без ключа cells. Позиционный список опознаётся по наличию элемента-строки или null; список из одних объектов читается как обычный — для простой строки обе трактовки дают один результат. Три дефекта, вскрытых проверкой на потери (каждый раз XML переставал совпадать с прежним, и это приводило к причине): - компилятор двигал позицию на единицу за элемент, поэтому объектный элемент со span: 3 занимал одну позицию вместо трёх и всё правее него уезжало влево. У маркеров ">" этого не было — каждый съедает свою позицию; - проход «удалить неиспользуемые стили» не заглядывал в строки-массивы: у массива нет свойства cells, и стиль, использованный только внутри такой строки, вырезался как неиспользуемый; - в py тот же проход падал на позиционном списке — "style" in c для None бросает TypeError, а для строки делает подстрочный поиск. Плюс ловушка PowerShell: += разворачивает вложенный массив, и строка-массив расплющивалась в список строк — нужна запятая. Проверено: XML на всех 40 макетах пилота совпал с прежним БАЙТ В БАЙТ, то есть сокращение без потерь. Объём декомпилированного JSON меньше на 11%. Тесты 54/54 на обоих рантаймах, гарды зелёные. В WORKFLOW уточнена находка про паритет портов: декомпиляторы расходятся по JSON на 33 макетах из 40, а компиляторы на одном и том же входе — всего на 2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a9697b613d
commit
4cceb03269
@@ -1,4 +1,4 @@
|
|||||||
# mxl-compile v1.24 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи)
|
# mxl-compile v1.25 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи)
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
@@ -700,6 +700,13 @@ function Expand-ShorthandRow {
|
|||||||
$cells += $cell
|
$cells += $cell
|
||||||
$placed[$idx] = $cell
|
$placed[$idx] = $cell
|
||||||
$last = $cell
|
$last = $cell
|
||||||
|
# Ячейка занимает СТОЛЬКО позиций, каков её span. У строки со следующими ">" это
|
||||||
|
# получается само (каждый маркер съедает позицию), а объектный элемент несёт span
|
||||||
|
# внутри — без этого сдвига всё правее него уезжало влево.
|
||||||
|
$elSpan = if ($cell.span) { [int]$cell.span } else { 1 }
|
||||||
|
if ($elSpan -gt 1) {
|
||||||
|
for ($s = 1; $s -lt $elSpan; $s++) { $idx++; $placed[$idx] = $cell }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Колонки, не занятые в этой строке, теряют «ячейку сверху».
|
# Колонки, не занятые в этой строке, теряют «ячейку сверху».
|
||||||
@@ -709,6 +716,18 @@ function Expand-ShorthandRow {
|
|||||||
return [PSCustomObject]@{ cells = $cells }
|
return [PSCustomObject]@{ cells = $cells }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Позиционный список ячеек опознаём по наличию хотя бы одного элемента-строки или null:
|
||||||
|
# маркеры, текст и пропуски бывают только в нём. Список из одних объектов разбирается
|
||||||
|
# как обычный — для простой строки обе трактовки дают один результат, неоднозначности нет.
|
||||||
|
function Test-PositionalCells {
|
||||||
|
param($cells)
|
||||||
|
if ($null -eq $cells -or -not ($cells -is [array])) { return $false }
|
||||||
|
foreach ($el in $cells) {
|
||||||
|
if ($null -eq $el -or $el -is [string]) { return $true }
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
# Карту занятых колонок ведём и по объектным строкам: "|" продолжает ту ячейку,
|
# Карту занятых колонок ведём и по объектным строкам: "|" продолжает ту ячейку,
|
||||||
# которая реально стоит выше, независимо от того, какой формой её записали.
|
# которая реально стоит выше, независимо от того, какой формой её записали.
|
||||||
function Update-OpenByCol {
|
function Update-OpenByCol {
|
||||||
@@ -743,7 +762,14 @@ foreach ($area in $def.areas) {
|
|||||||
foreach ($row in $area.rows) {
|
foreach ($row in $area.rows) {
|
||||||
$rowIdx++
|
$rowIdx++
|
||||||
if ($row -is [array]) {
|
if ($row -is [array]) {
|
||||||
|
# Строка целиком массивом — сахар для { cells: [...] }.
|
||||||
$expandedRows += Expand-ShorthandRow -row $row -areaName $areaName -rowIdx $rowIdx -openByCol $openByCol -maxCols $areaMaxCols
|
$expandedRows += Expand-ShorthandRow -row $row -areaName $areaName -rowIdx $rowIdx -openByCol $openByCol -maxCols $areaMaxCols
|
||||||
|
} elseif (Test-PositionalCells $row.cells) {
|
||||||
|
# Короткая запись — свойство СПИСКА ЯЧЕЕК, а не строки: свои height и rowStyle
|
||||||
|
# строка при этом сохраняет.
|
||||||
|
$expanded = Expand-ShorthandRow -row $row.cells -areaName $areaName -rowIdx $rowIdx -openByCol $openByCol -maxCols $areaMaxCols
|
||||||
|
$row.cells = $expanded.cells
|
||||||
|
$expandedRows += $row
|
||||||
} else {
|
} else {
|
||||||
$expandedRows += $row
|
$expandedRows += $row
|
||||||
if ($row.empty) { $openByCol.Clear() } else { Update-OpenByCol -row $row -openByCol $openByCol }
|
if ($row.empty) { $openByCol.Clear() } else { Update-OpenByCol -row $row -openByCol $openByCol }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# mxl-compile v1.24 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи)
|
# mxl-compile v1.25 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи)
|
||||||
# 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
|
||||||
@@ -674,7 +674,9 @@ def main():
|
|||||||
extended = [] # ячейки, чей rowspan уже нарастили в этой строке (span>1 даёт несколько "|")
|
extended = [] # ячейки, чей rowspan уже нарастили в этой строке (span>1 даёт несколько "|")
|
||||||
last = None # последняя реальная ячейка слева — цель для ">"
|
last = None # последняя реальная ячейка слева — цель для ">"
|
||||||
|
|
||||||
for idx, el in enumerate(row, start=1):
|
idx = 0
|
||||||
|
for el in row:
|
||||||
|
idx += 1
|
||||||
if idx > max_cols:
|
if idx > max_cols:
|
||||||
print(f'Row exceeds \'columns\' ({max_cols}): area "{area_name}",'
|
print(f'Row exceeds \'columns\' ({max_cols}): area "{area_name}",'
|
||||||
f' row {row_idx}', file=sys.stderr)
|
f' row {row_idx}', file=sys.stderr)
|
||||||
@@ -729,6 +731,13 @@ def main():
|
|||||||
cells.append(cell)
|
cells.append(cell)
|
||||||
placed[idx] = cell
|
placed[idx] = cell
|
||||||
last = cell
|
last = cell
|
||||||
|
# Ячейка занимает СТОЛЬКО позиций, каков её span. У строки со следующими ">" это
|
||||||
|
# получается само (каждый маркер съедает позицию), а объектный элемент несёт span
|
||||||
|
# внутри — без этого сдвига всё правее него уезжало влево.
|
||||||
|
el_span = int(cell.get('span') or 1)
|
||||||
|
for _ in range(el_span - 1):
|
||||||
|
idx += 1
|
||||||
|
placed[idx] = cell
|
||||||
|
|
||||||
# Колонки, не занятые в этой строке, теряют «ячейку сверху».
|
# Колонки, не занятые в этой строке, теряют «ячейку сверху».
|
||||||
open_by_col.clear()
|
open_by_col.clear()
|
||||||
@@ -738,6 +747,14 @@ def main():
|
|||||||
out['cells'] = cells
|
out['cells'] = cells
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
# Позиционный список ячеек опознаём по наличию хотя бы одного элемента-строки или None:
|
||||||
|
# маркеры, текст и пропуски бывают только в нём. Список из одних объектов разбирается
|
||||||
|
# как обычный — для простой строки обе трактовки дают один результат, неоднозначности нет.
|
||||||
|
def is_positional_cells(cells):
|
||||||
|
if not isinstance(cells, list):
|
||||||
|
return False
|
||||||
|
return any(el is None or isinstance(el, str) for el in cells)
|
||||||
|
|
||||||
# Карту занятых колонок ведём и по объектным строкам: "|" продолжает ту ячейку,
|
# Карту занятых колонок ведём и по объектным строкам: "|" продолжает ту ячейку,
|
||||||
# которая реально стоит выше, независимо от того, какой формой её записали.
|
# которая реально стоит выше, независимо от того, какой формой её записали.
|
||||||
def update_open_by_col(row, open_by_col):
|
def update_open_by_col(row, open_by_col):
|
||||||
@@ -767,7 +784,14 @@ def main():
|
|||||||
expanded_rows = []
|
expanded_rows = []
|
||||||
for row_idx, row in enumerate(area.get('rows', []), start=1):
|
for row_idx, row in enumerate(area.get('rows', []), start=1):
|
||||||
if isinstance(row, list):
|
if isinstance(row, list):
|
||||||
|
# Строка целиком массивом — сахар для { cells: [...] }.
|
||||||
expanded_rows.append(expand_shorthand_row(row, area_name, row_idx, open_by_col, area_max_cols))
|
expanded_rows.append(expand_shorthand_row(row, area_name, row_idx, open_by_col, area_max_cols))
|
||||||
|
elif is_positional_cells(row.get('cells')):
|
||||||
|
# Короткая запись — свойство СПИСКА ЯЧЕЕК, а не строки: свои height и rowStyle
|
||||||
|
# строка при этом сохраняет.
|
||||||
|
expanded = expand_shorthand_row(row['cells'], area_name, row_idx, open_by_col, area_max_cols)
|
||||||
|
row['cells'] = expanded['cells']
|
||||||
|
expanded_rows.append(row)
|
||||||
else:
|
else:
|
||||||
expanded_rows.append(row)
|
expanded_rows.append(row)
|
||||||
if row.get('empty'):
|
if row.get('empty'):
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# mxl-decompile v1.7 — Decompile 1C spreadsheet to JSON
|
# mxl-decompile v1.8 — 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)]
|
||||||
@@ -480,6 +480,54 @@ function Get-StyleName {
|
|||||||
return "default"
|
return "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Список признаётся позиционным по тому же правилу, что и в компиляторе: есть элемент-строка
|
||||||
|
# или пропуск. Список из одних объектов таковым не считаем — он читается как обычный.
|
||||||
|
function Test-PositionalList {
|
||||||
|
param($cells)
|
||||||
|
foreach ($el in $cells) {
|
||||||
|
if ($null -eq $el -or $el -is [string]) { return $true }
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Позиционная запись списка ячеек: позиция берётся из порядка, `col` не пишется.
|
||||||
|
# Применяем, когда первая ячейка стоит в колонке 1 — иначе список начнётся с череды null
|
||||||
|
# и станет длиннее объектного. Ячейка, у которой кроме текста или параметра ничего нет,
|
||||||
|
# пишется строкой; span раскрывается маркерами ">"; всё прочее — объектным элементом без col.
|
||||||
|
function ConvertTo-PositionalCells {
|
||||||
|
param($cells)
|
||||||
|
if ($cells.Count -eq 0) { return @() }
|
||||||
|
$first = $cells[0]
|
||||||
|
if ([int]$first["col"] -ne 1) { return $cells }
|
||||||
|
|
||||||
|
$out = @()
|
||||||
|
$expected = 1
|
||||||
|
foreach ($c in $cells) {
|
||||||
|
$col = [int]$c["col"]
|
||||||
|
if ($col -lt $expected) { return $cells } # перекрытие — позиционно не выразить
|
||||||
|
while ($expected -lt $col) { $out += $null; $expected++ }
|
||||||
|
|
||||||
|
$span = if ($c.Contains("span")) { [int]$c["span"] } else { 1 }
|
||||||
|
$keys = @($c.Keys | Where-Object { $_ -notin @("col", "span") })
|
||||||
|
$plainText = ($keys.Count -eq 1 -and $keys[0] -eq "text" -and $c["text"] -is [string])
|
||||||
|
$plainParam = ($keys.Count -eq 1 -and $keys[0] -eq "param")
|
||||||
|
|
||||||
|
if ($plainText) { $out += $c["text"] }
|
||||||
|
elseif ($plainParam) { $out += "{$($c["param"])}" }
|
||||||
|
else {
|
||||||
|
$obj = [ordered]@{}
|
||||||
|
foreach ($k in $c.Keys) { if ($k -ne "col") { $obj[$k] = $c[$k] } }
|
||||||
|
$out += $obj
|
||||||
|
}
|
||||||
|
# span раскрываем маркерами — кроме объектного элемента, он несёт span сам.
|
||||||
|
if (($plainText -or $plainParam) -and $span -gt 1) {
|
||||||
|
for ($i = 1; $i -lt $span; $i++) { $out += ">" }
|
||||||
|
}
|
||||||
|
$expected = $col + $span
|
||||||
|
}
|
||||||
|
return $out
|
||||||
|
}
|
||||||
|
|
||||||
# --- 12. Build areas ---
|
# --- 12. Build areas ---
|
||||||
|
|
||||||
# Сетка нарезается на блоки: непересекающиеся области типа Rows задают границы, строки вне
|
# Сетка нарезается на блоки: непересекающиеся области типа Rows задают границы, строки вне
|
||||||
@@ -664,8 +712,14 @@ foreach ($area in $blocks) {
|
|||||||
$dslCells += $dslCell
|
$dslCells += $dslCell
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dslCells.Count -gt 0) { $dslRow["cells"] = [array]$dslCells }
|
if ($dslCells.Count -gt 0) { $dslRow["cells"] = [array](ConvertTo-PositionalCells $dslCells) }
|
||||||
$areaRows += $dslRow
|
# Самая короткая из применимых форм: если у строки нет своих свойств, а список ячеек
|
||||||
|
# позиционный — строка пишется просто массивом, без ключа cells.
|
||||||
|
if ($dslRow.Count -eq 1 -and $dslRow.Contains("cells") -and (Test-PositionalList $dslRow["cells"])) {
|
||||||
|
$areaRows += ,([array]$dslRow["cells"])
|
||||||
|
} else {
|
||||||
|
$areaRows += $dslRow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Compress consecutive empty rows ({}) into { empty = N }
|
# Compress consecutive empty rows ({}) into { empty = N }
|
||||||
@@ -680,7 +734,8 @@ foreach ($area in $blocks) {
|
|||||||
else { $compressedRows += [ordered]@{ empty = $emptyRun } }
|
else { $compressedRows += [ordered]@{ empty = $emptyRun } }
|
||||||
$emptyRun = 0
|
$emptyRun = 0
|
||||||
}
|
}
|
||||||
$compressedRows += $r
|
# Запятая обязательна: строка-массив иначе развернётся в список строк.
|
||||||
|
$compressedRows += ,$r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($emptyRun -gt 0) {
|
if ($emptyRun -gt 0) {
|
||||||
@@ -755,8 +810,11 @@ if ($styleDefs.Contains("default") -and $styleDefs["default"].Count -eq 0) {
|
|||||||
$usedStyles = @{}
|
$usedStyles = @{}
|
||||||
foreach ($a in $dslAreas) {
|
foreach ($a in $dslAreas) {
|
||||||
foreach ($r in $a.rows) {
|
foreach ($r in $a.rows) {
|
||||||
if ($r.rowStyle) { $usedStyles[$r.rowStyle] = $true }
|
# Строка может быть массивом (короткая форма) — у неё нет свойства cells, и без этой
|
||||||
if ($r.cells) { foreach ($c in $r.cells) { if ($c.style) { $usedStyles[$c.style] = $true } } }
|
# ветки стиль, использованный только внутри такой строки, вырезался как «неиспользуемый».
|
||||||
|
$cellList = if ($r -is [array]) { $r } else { $r.cells }
|
||||||
|
if ($r -isnot [array] -and $r.rowStyle) { $usedStyles[$r.rowStyle] = $true }
|
||||||
|
if ($cellList) { foreach ($c in $cellList) { if ($c -isnot [string] -and $c.style) { $usedStyles[$c.style] = $true } } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$toRemove = @($styleDefs.Keys | Where-Object { -not $usedStyles.ContainsKey($_) })
|
$toRemove = @($styleDefs.Keys | Where-Object { -not $usedStyles.ContainsKey($_) })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# mxl-decompile v1.7 — Decompile 1C spreadsheet to JSON
|
# mxl-decompile v1.8 — 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
|
||||||
@@ -667,6 +667,39 @@ def main():
|
|||||||
return style_names[key]
|
return style_names[key]
|
||||||
return "default"
|
return "default"
|
||||||
|
|
||||||
|
def to_positional_cells(cells):
|
||||||
|
"""Позиционная запись списка ячеек: позиция берётся из порядка, `col` не пишется.
|
||||||
|
Применяем, когда первая ячейка стоит в колонке 1 — иначе список начнётся с череды None
|
||||||
|
и станет длиннее объектного. Ячейка, у которой кроме текста или параметра ничего нет,
|
||||||
|
пишется строкой; span раскрывается маркерами ">"; прочее — объектным элементом без col."""
|
||||||
|
if not cells or int(cells[0].get("col", 0)) != 1:
|
||||||
|
return cells
|
||||||
|
out = []
|
||||||
|
expected = 1
|
||||||
|
for c in cells:
|
||||||
|
col = int(c.get("col", 0))
|
||||||
|
if col < expected:
|
||||||
|
return cells # перекрытие — позиционно не выразить
|
||||||
|
while expected < col:
|
||||||
|
out.append(None)
|
||||||
|
expected += 1
|
||||||
|
span = int(c.get("span", 1) or 1)
|
||||||
|
keys = [k for k in c if k not in ("col", "span")]
|
||||||
|
plain_text = len(keys) == 1 and keys[0] == "text" and isinstance(c["text"], str)
|
||||||
|
plain_param = len(keys) == 1 and keys[0] == "param"
|
||||||
|
if plain_text:
|
||||||
|
out.append(c["text"])
|
||||||
|
elif plain_param:
|
||||||
|
out.append("{%s}" % c["param"])
|
||||||
|
else:
|
||||||
|
obj = OrderedDict((k, v) for k, v in c.items() if k != "col")
|
||||||
|
out.append(obj)
|
||||||
|
# span раскрываем маркерами — кроме объектного элемента, он несёт span сам.
|
||||||
|
if (plain_text or plain_param) and span > 1:
|
||||||
|
out.extend([">"] * (span - 1))
|
||||||
|
expected = col + span
|
||||||
|
return out
|
||||||
|
|
||||||
# --- 12. Build areas ---
|
# --- 12. Build areas ---
|
||||||
|
|
||||||
# Сетка нарезается на блоки: непересекающиеся области типа Rows задают границы, строки вне
|
# Сетка нарезается на блоки: непересекающиеся области типа Rows задают границы, строки вне
|
||||||
@@ -831,8 +864,14 @@ def main():
|
|||||||
dsl_cells.append(dsl_cell)
|
dsl_cells.append(dsl_cell)
|
||||||
|
|
||||||
if len(dsl_cells) > 0:
|
if len(dsl_cells) > 0:
|
||||||
dsl_row["cells"] = dsl_cells
|
dsl_row["cells"] = to_positional_cells(dsl_cells)
|
||||||
area_rows.append(dsl_row)
|
# Самая короткая из применимых форм: если у строки нет своих свойств, а список ячеек
|
||||||
|
# позиционный — строка пишется просто массивом, без ключа cells.
|
||||||
|
if (len(dsl_row) == 1 and "cells" in dsl_row
|
||||||
|
and any(el is None or isinstance(el, str) for el in dsl_row["cells"])):
|
||||||
|
area_rows.append(dsl_row["cells"])
|
||||||
|
else:
|
||||||
|
area_rows.append(dsl_row)
|
||||||
|
|
||||||
# Compress consecutive empty rows ({}) into { empty = N }
|
# Compress consecutive empty rows ({}) into { empty = N }
|
||||||
compressed_rows = []
|
compressed_rows = []
|
||||||
@@ -933,12 +972,19 @@ def main():
|
|||||||
used_styles = set()
|
used_styles = set()
|
||||||
for a in dsl_areas:
|
for a in dsl_areas:
|
||||||
for r in a["rows"]:
|
for r in a["rows"]:
|
||||||
if "rowStyle" in r:
|
# Строка может быть массивом (короткая форма) — у неё нет ключа cells, и без этой
|
||||||
used_styles.add(r["rowStyle"])
|
# ветки стиль, использованный только внутри такой строки, вырезался как «неиспользуемый».
|
||||||
if "cells" in r:
|
if isinstance(r, list):
|
||||||
for c in r["cells"]:
|
cell_list = r
|
||||||
if "style" in c:
|
else:
|
||||||
used_styles.add(c["style"])
|
if "rowStyle" in r:
|
||||||
|
used_styles.add(r["rowStyle"])
|
||||||
|
cell_list = r.get("cells") or []
|
||||||
|
# Список ячеек может быть позиционным: строки, None и маркеры стиля не несут,
|
||||||
|
# стиль бывает только у объектного элемента.
|
||||||
|
for c in cell_list:
|
||||||
|
if isinstance(c, dict) and "style" in c:
|
||||||
|
used_styles.add(c["style"])
|
||||||
to_remove = [s for s in style_defs if s not in used_styles]
|
to_remove = [s for s in style_defs if s not in used_styles]
|
||||||
for s in to_remove:
|
for s in to_remove:
|
||||||
del style_defs[s]
|
del style_defs[s]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{ "columns": 2, "defaultWidth": 10, "fonts": { "default": { "face": "Arial", "size": 10 } }, "styles": {}, "areas": [{ "name": "Test", "rows": [{ "cells": [{ "col": 1, "text": "A" }, { "col": 2, "text": "B" }] }] }] }
|
{ "columns": 2, "defaultWidth": 10, "fonts": { "default": { "face": "Arial", "size": 10 } }, "styles": {}, "areas": [{ "name": "Test", "rows": [["A", "B"]] }] }
|
||||||
Reference in New Issue
Block a user