mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-19 09:40:22 +03:00
feat(mxl-compile,mxl-decompile): колонтитулы и параметры печати
Ключи header и footer описывают колонтитул: три слота (left, center, right), общий шрифт и вертикальное выравнивание, признак вывода и страница, с которой печатать. Ключ printSettings — плоский набор параметров печати с именами тегов платформы; порядок компилятор выставляет сам, незнакомый ключ отвергает. Слот устроен как ячейка: ссылка на формат плюс текст. Текст бывает двух видов, и это не текст против шаблона, а обычная строка против форматированной: у форматированной разметка живёт прямо в содержимом (<b>жирный</>, <fontsize 12>, <colorstyle -16>), поэтому она возится как есть. Признак вывода и стартовая страница лежат в формате как <height> и <width>, но читаются только у записи с <height>: палитра дедуплицирована, и колонтитул без своих настроек ссылается на чужую запись, где <width> — ширина колонки (637 таких ссылок против 153 своих). Перенос строки внутри текста колонтитула платформа хранит голым LF при CRLF во всём остальном файле — единственное такое место в документе, поэтому содержимое пишется одной строкой вывода. Формат колонтитула регистрируется до отсева неиспользуемых шрифтов: иначе шрифт, на который ссылается только колонтитул, выбрасывался бы из палитры. Стенд Колонтитулы собирается байт в байт обоими портами и добавлен в платформенные фикстуры. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1e0b82d419
commit
702a3b6a8d
@@ -64,6 +64,8 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J
|
||||
namedAreas: [{ name, rows, cols }],
|
||||
rowGroups: [{ rows, name, collapsed, titleLocation }],
|
||||
columnGroups: [{ cols, name, collapsed, titleLocation }],
|
||||
header: { left, center, right, font, verticalAlignment, show, startPage }, footer: { … },
|
||||
printSettings: { pageOrientation, topMargin, …, fitToPage, firstPageNumber },
|
||||
columnSets: { name: { columns, columnWidths, columnStyles } }
|
||||
}
|
||||
```
|
||||
@@ -83,6 +85,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J
|
||||
- `rowspan` — объединение строк вниз (rowStyle учитывает занятые ячейки)
|
||||
- Содержимое ячейки задаётся одним из ключей: `param` — параметр заполнения, `text` — статический текст, `template` — текст со вставками `[Параметр]`
|
||||
- `rowGroups`/`columnGroups` — группы строк и колонок (сворачиваются кнопкой на полях): плоский список диапазонов, вложенность выражена вхождением одного в другой
|
||||
- `header`/`footer` — колонтитулы: три слота (`left`/`center`/`right`), общий шрифт и вертикальное выравнивание; текст может нести поля `[&НомерСтраницы]`, `[&Дата]`
|
||||
- `note` — всплывающая подсказка у ячейки: строка, объект языков или `{ text, style, autoSize, box }`
|
||||
- `valueType` делает ячейку полем ввода (`"Number(15,3,nonneg)"`, `"String(10)"`, `"CatalogRef.Валюты"`, составной через ` + `); текста в такой ячейке быть не может, значение задаётся ключом `value`
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@
|
||||
| `columnSets` | нет | — | Дополнительные колоночные раскладки (см. `styles.md`) |
|
||||
| `rowGroups` | нет | — | Группы строк (см. ниже) |
|
||||
| `columnGroups` | нет | — | Группы колонок (см. ниже) |
|
||||
| `header` / `footer` | нет | — | Верхний и нижний колонтитулы (см. ниже) |
|
||||
| `printSettings` | нет | — | Параметры печати (см. ниже) |
|
||||
|
||||
## Области (`areas[]`)
|
||||
|
||||
@@ -296,6 +298,56 @@
|
||||
Координаты ячейки в примечании не задаются. `autoSize` и `box` независимы: при автоподгоне
|
||||
размера положение окошка всё равно хранится.
|
||||
|
||||
## Колонтитулы
|
||||
|
||||
У верхнего и нижнего колонтитула по три слота — `left`, `center`, `right` — и общие настройки:
|
||||
|
||||
```json
|
||||
"header": {
|
||||
"font": "мелкий",
|
||||
"verticalAlignment": "Bottom",
|
||||
"startPage": 2,
|
||||
"left": "Слева\nвторая строка",
|
||||
"center": "Стр. [&НомерСтраницы] из [&СтраницВсего]",
|
||||
"right": { "ru": "Справа", "en": "Right" }
|
||||
},
|
||||
"footer": {
|
||||
"show": false,
|
||||
"center": { "formatted": "<b>Итог</> <colorstyle -16>красным</>" }
|
||||
}
|
||||
```
|
||||
|
||||
| Поле | По умолч. | Описание |
|
||||
|------|-----------|----------|
|
||||
| `left`, `center`, `right` | — | Текст слота: строка, объект «язык → текст» или `{ "formatted": … }` |
|
||||
| `font` | — | Имя шрифта из `fonts` — на весь колонтитул |
|
||||
| `verticalAlignment` | — | Положение текста по вертикали: `Top`, `Center`, `Bottom` |
|
||||
| `show` | `true` | Выводить ли колонтитул |
|
||||
| `startPage` | `1` | Страница, с которой колонтитул печатается |
|
||||
|
||||
Текст может быть многострочным (`\n`) и содержать поля `[&НомерСтраницы]`, `[&СтраницВсего]`,
|
||||
`[&Дата]`, `[&Время]` — платформа подставляет их при печати. `{ "formatted": … }` — форматированная
|
||||
строка: разметка живёт прямо в тексте (`<b>жирный</>`, `<fontsize 12>`, `<colorstyle -16>`).
|
||||
|
||||
## Параметры печати
|
||||
|
||||
Плоский объект; имя ключа совпадает с именем свойства в выгрузке:
|
||||
|
||||
```json
|
||||
"printSettings": {
|
||||
"pageOrientation": "Landscape",
|
||||
"topMargin": 500, "leftMargin": 500, "bottomMargin": 500, "rightMargin": 500,
|
||||
"headerSize": 1000, "footerSize": 1000,
|
||||
"fitToPage": true, "firstPageNumber": 7
|
||||
}
|
||||
```
|
||||
|
||||
Допустимые ключи: `pageOrientation`, `scale`, `collate`, `copies`, `perPage`, `topMargin`,
|
||||
`leftMargin`, `bottomMargin`, `rightMargin`, `headerSize`, `footerSize`, `fitToPage`,
|
||||
`blackAndWhite`, `printerName`, `paper`, `paperSource`, `pageWidth`, `pageHeight`, `duplexType`,
|
||||
`pagePlacementAlternation`, `firstPageNumber`. Незнакомый ключ — ошибка. Порядок в объекте не
|
||||
важен: компилятор пишет свойства в том порядке, что и платформа.
|
||||
|
||||
## Группы строк и колонок
|
||||
|
||||
Диапазон строк или колонок, который сворачивается кнопкой на полях. Задаются плоским списком;
|
||||
@@ -339,7 +391,7 @@ XML не возвращается.
|
||||
|
||||
- объединения, не привязанные к ячейке (по всей высоте или ширине документа);
|
||||
- рисунки и картинки, в том числе штрихкоды;
|
||||
- колонтитулы, параметры печати, область печати.
|
||||
- область печати.
|
||||
|
||||
Пересборка макета из DSL — это полная перегенерация, а не точечная правка XML, поэтому
|
||||
diff после round-trip обычно шире фактической доработки.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# mxl-compile v1.44 — Compile 1C spreadsheet from JSON
|
||||
# mxl-compile v1.45 — Compile 1C spreadsheet from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -1485,9 +1485,108 @@ foreach ($area in $def.areas) {
|
||||
}
|
||||
}
|
||||
|
||||
# 7d-ter. Колонтитулы. Шесть слотов: left/center/right у верхнего и нижнего. Слот устроен
|
||||
# как ячейка — ссылка на формат плюс текст, — а формат несёт то, что в диалоге задаётся на весь
|
||||
# колонтитул: шрифт, вертикальное выравнивание, признак вывода (<height> 1/-1) и страницу,
|
||||
# с которой колонтитул печатается (<width>).
|
||||
$script:headerSlots = @('left', 'center', 'right')
|
||||
|
||||
function Get-HeaderFormatIndex {
|
||||
param($hf)
|
||||
if ($null -eq $hf) { return 0 }
|
||||
$props = @{}
|
||||
if ($hf.font -and $fontMap.Contains("$($hf.font)")) { $props['font'] = $fontMap["$($hf.font)"] }
|
||||
# Признак вывода и стартовая страница осмысленны только вместе: платформа пишет их парой.
|
||||
$props['height'] = if ($hf.show -eq $false -or "$($hf.show)" -eq 'false') { -1 } else { 1 }
|
||||
$props['width'] = if ($hf.startPage) { [int]$hf.startPage } else { 1 }
|
||||
if ($hf.verticalAlignment) { $props['verticalAlignment'] = "$($hf.verticalAlignment)" }
|
||||
return Register-Format $props
|
||||
}
|
||||
|
||||
function Emit-HeaderSlot {
|
||||
param([string]$tag, $value, [int]$fmtIdx)
|
||||
if ($null -eq $value) { return }
|
||||
# Форматированная строка платформы — тот же текст, только разметка живёт внутри содержимого
|
||||
# (<b>жирный</>, <fontsize 12>…), поэтому возим её как есть и различаем именем тега.
|
||||
$formatted = $false
|
||||
$text = $value
|
||||
if ($value -is [System.Management.Automation.PSCustomObject] -and $null -ne $value.formatted) {
|
||||
$formatted = $true
|
||||
$text = $value.formatted
|
||||
}
|
||||
$pairs = @()
|
||||
if ($text -is [System.Collections.IDictionary]) {
|
||||
foreach ($k in $text.Keys) { $pairs += @{ Lang = "$k"; Text = "$($text[$k])" } }
|
||||
} elseif ($text -is [System.Management.Automation.PSCustomObject]) {
|
||||
foreach ($pr in $text.PSObject.Properties) { $pairs += @{ Lang = $pr.Name; Text = "$($pr.Value)" } }
|
||||
} else {
|
||||
foreach ($l in $textLanguages) { $pairs += @{ Lang = $l; Text = "$text" } }
|
||||
}
|
||||
$textTag = if ($formatted) { 'tfl' } else { 'tl' }
|
||||
X "`t<$tag>"
|
||||
X "`t`t<f>$fmtIdx</f>"
|
||||
if ($pairs.Count -eq 0) {
|
||||
X "`t`t<$textTag/>"
|
||||
} else {
|
||||
X "`t`t<$textTag>"
|
||||
foreach ($pr in $pairs) {
|
||||
X "`t`t`t<v8:item>"
|
||||
X "`t`t`t`t<v8:lang>$($pr.Lang)</v8:lang>"
|
||||
# Перенос строки внутри колонтитула платформа хранит голым LF, тогда как весь файл
|
||||
# идёт CRLF. Пишем содержимое одной строкой вывода, чтобы перенос не переписался.
|
||||
X "`t`t`t`t<v8:content>$(Esc-XmlText $pr.Text)</v8:content>"
|
||||
X "`t`t`t</v8:item>"
|
||||
}
|
||||
X "`t`t</$textTag>"
|
||||
}
|
||||
X "`t</$tag>"
|
||||
}
|
||||
|
||||
# Параметры печати. Имя ключа совпадает с именем тега в выгрузке, как у свойств стиля.
|
||||
# Порядок снят с корпуса (2350 макетов): платформа пишет теги строго в этой последовательности.
|
||||
function Get-PrintSettingsOrder {
|
||||
return @(
|
||||
'pageOrientation', 'scale', 'collate', 'copies', 'perPage',
|
||||
'topMargin', 'leftMargin', 'bottomMargin', 'rightMargin', 'headerSize', 'footerSize',
|
||||
'fitToPage', 'blackAndWhite', 'printerName', 'paper', 'paperSource',
|
||||
'pageWidth', 'pageHeight', 'duplexType', 'pagePlacementAlternation', 'firstPageNumber'
|
||||
)
|
||||
}
|
||||
|
||||
function Emit-PrintSettings {
|
||||
param($ps)
|
||||
if ($null -eq $ps) { return }
|
||||
$order = Get-PrintSettingsOrder
|
||||
$known = @{}
|
||||
foreach ($k in $order) { $known[$k] = $true }
|
||||
foreach ($pr in $ps.PSObject.Properties) {
|
||||
if (-not $known.ContainsKey($pr.Name)) {
|
||||
[Console]::Error.WriteLine("printSettings: unknown key `"$($pr.Name)`". Allowed: $($order -join ', ')")
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
$emitted = @()
|
||||
foreach ($tag in $order) {
|
||||
$v = $ps.$tag
|
||||
if ($null -eq $v) { continue }
|
||||
if ($v -is [bool]) { $v = if ($v) { 'true' } else { 'false' } }
|
||||
$emitted += "`t`t<$tag>$(Esc-XmlText "$v")</$tag>"
|
||||
}
|
||||
if ($emitted.Count -eq 0) { return }
|
||||
X "`t<printSettings>"
|
||||
foreach ($line in $emitted) { X $line }
|
||||
X "`t</printSettings>"
|
||||
}
|
||||
|
||||
# Формат по умолчанию — последняя запись палитры (см. выше).
|
||||
$defaultFormatIndex = Register-Format @{ width = $defaultWidth }
|
||||
|
||||
# Формат колонтитула заводим здесь же: он идёт в палитре следом за умолчанием, и он
|
||||
# обязан попасть в реестр ДО отсева неиспользуемых шрифтов — иначе шрифт колонтитула,
|
||||
# на который больше никто не ссылается, будет выброшен.
|
||||
$headerFmt = Get-HeaderFormatIndex $def.header
|
||||
$footerFmt = Get-HeaderFormatIndex $def.footer
|
||||
|
||||
# Шрифт, на который не ссылается ни один формат, платформа в палитру не кладёт: у макета
|
||||
# без оформления элемента <font> нет вовсе. Мы же всегда заводили Arial 10 по умолчанию.
|
||||
# Отбрасываем неиспользуемые и перенумеровываем ссылки — индексы шрифтов позиционные.
|
||||
@@ -2037,6 +2136,14 @@ if ($def.namedAreas) {
|
||||
}
|
||||
}
|
||||
|
||||
# 7d-ter. Колонтитулы: шесть слотов идут после строк и перед скалярными свойствами документа.
|
||||
foreach ($slot in $script:headerSlots) {
|
||||
if ($def.header) { Emit-HeaderSlot "$($slot)Header" $def.header.$slot $headerFmt }
|
||||
}
|
||||
foreach ($slot in $script:headerSlots) {
|
||||
if ($def.footer) { Emit-HeaderSlot "$($slot)Footer" $def.footer.$slot $footerFmt }
|
||||
}
|
||||
|
||||
# 7e-bis. Группировки строк и колонок. Платформа хранит их плоским списком диапазонов;
|
||||
# вложенность выражена вхождением одного диапазона в другой, а частичных пересечений не
|
||||
# бывает вовсе (корпус: 40 620 886 пар непересекающихся, 599 958 вложенных, 0 частичных).
|
||||
@@ -2156,6 +2263,8 @@ X "`t<vgRows>$totalRowCount</vgRows>"
|
||||
Emit-Groups 'vg' $rowGroups
|
||||
Emit-Groups 'hg' $colGroups
|
||||
|
||||
Emit-PrintSettings $def.printSettings
|
||||
|
||||
# 7f. Merges
|
||||
foreach ($m in $merges) {
|
||||
X "`t<merge>"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# mxl-compile v1.44 — Compile 1C spreadsheet from JSON
|
||||
# mxl-compile v1.45 — Compile 1C spreadsheet from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import hashlib
|
||||
@@ -1462,9 +1462,92 @@ def main():
|
||||
if note:
|
||||
register_note_format(note)
|
||||
|
||||
# Колонтитулы. Шесть слотов: left/center/right у верхнего и нижнего. Слот устроен как
|
||||
# ячейка — ссылка на формат плюс текст, — а формат несёт то, что в диалоге задаётся на весь
|
||||
# колонтитул: шрифт, вертикальное выравнивание, признак вывода (<height> 1/-1) и страницу,
|
||||
# с которой колонтитул печатается (<width>).
|
||||
def header_format_index(hf):
|
||||
if not hf:
|
||||
return 0
|
||||
props = {}
|
||||
if hf.get('font') and str(hf['font']) in font_map:
|
||||
props['font'] = font_map[str(hf['font'])]
|
||||
# Признак вывода и стартовая страница осмысленны только вместе: платформа пишет их парой.
|
||||
props['height'] = -1 if (hf.get('show') is False or str(hf.get('show')).lower() == 'false') else 1
|
||||
props['width'] = int(hf['startPage']) if hf.get('startPage') else 1
|
||||
if hf.get('verticalAlignment'):
|
||||
props['verticalAlignment'] = str(hf['verticalAlignment'])
|
||||
return register_format(props)
|
||||
|
||||
def emit_header_slot(lines, tag, value, fmt_idx):
|
||||
if value is None:
|
||||
return
|
||||
# Форматированная строка платформы — тот же текст, только разметка живёт внутри
|
||||
# содержимого (<b>жирный</>, <fontsize 12>…), поэтому возим её как есть и различаем
|
||||
# именем тега.
|
||||
formatted = isinstance(value, dict) and value.get('formatted') is not None
|
||||
text = value['formatted'] if formatted else value
|
||||
if isinstance(text, dict):
|
||||
pairs = [(str(k), str(v)) for k, v in text.items()]
|
||||
else:
|
||||
pairs = [(lang, str(text)) for lang in text_languages]
|
||||
text_tag = 'tfl' if formatted else 'tl'
|
||||
lines.append(f'\t<{tag}>')
|
||||
lines.append(f'\t\t<f>{fmt_idx}</f>')
|
||||
if not pairs:
|
||||
lines.append(f'\t\t<{text_tag}/>')
|
||||
else:
|
||||
lines.append(f'\t\t<{text_tag}>')
|
||||
for lang, content in pairs:
|
||||
lines.append('\t\t\t<v8:item>')
|
||||
lines.append(f'\t\t\t\t<v8:lang>{lang}</v8:lang>')
|
||||
# Перенос строки внутри колонтитула платформа хранит голым LF, тогда как весь
|
||||
# файл идёт CRLF. Содержимое кладём одним элементом списка, чтобы перенос
|
||||
# не переписался при склейке строк документа.
|
||||
lines.append(f'\t\t\t\t<v8:content>{esc_xml_text(content)}</v8:content>')
|
||||
lines.append('\t\t\t</v8:item>')
|
||||
lines.append(f'\t\t</{text_tag}>')
|
||||
lines.append(f'\t</{tag}>')
|
||||
|
||||
# Параметры печати. Имя ключа совпадает с именем тега в выгрузке, как у свойств стиля.
|
||||
# Порядок снят с корпуса (2350 макетов): платформа пишет теги строго в этой последовательности.
|
||||
def print_settings_order():
|
||||
return ('pageOrientation', 'scale', 'collate', 'copies', 'perPage',
|
||||
'topMargin', 'leftMargin', 'bottomMargin', 'rightMargin', 'headerSize', 'footerSize',
|
||||
'fitToPage', 'blackAndWhite', 'printerName', 'paper', 'paperSource',
|
||||
'pageWidth', 'pageHeight', 'duplexType', 'pagePlacementAlternation', 'firstPageNumber')
|
||||
|
||||
def emit_print_settings(lines, ps):
|
||||
if not ps:
|
||||
return
|
||||
order = print_settings_order()
|
||||
for key in ps:
|
||||
if key not in order:
|
||||
print(f'printSettings: unknown key "{key}". Allowed: {", ".join(order)}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
emitted = []
|
||||
for tag in order:
|
||||
v = ps.get(tag)
|
||||
if v is None:
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
v = 'true' if v else 'false'
|
||||
emitted.append(f'\t\t<{tag}>{esc_xml_text(str(v))}</{tag}>')
|
||||
if not emitted:
|
||||
return
|
||||
lines.append('\t<printSettings>')
|
||||
lines.extend(emitted)
|
||||
lines.append('\t</printSettings>')
|
||||
|
||||
# Формат по умолчанию — последняя запись палитры (см. выше).
|
||||
default_format_index = register_format({'width': default_width})
|
||||
|
||||
# Формат колонтитула заводим здесь же: он идёт в палитре следом за умолчанием, и он
|
||||
# обязан попасть в реестр ДО отсева неиспользуемых шрифтов — иначе шрифт колонтитула,
|
||||
# на который больше никто не ссылается, будет выброшен.
|
||||
header_fmt = header_format_index(defn.get('header'))
|
||||
footer_fmt = header_format_index(defn.get('footer'))
|
||||
|
||||
# Шрифт, на который не ссылается ни один формат, платформа в палитру не кладёт: у макета
|
||||
# без оформления элемента <font> нет вовсе. Мы же всегда заводили Arial 10 по умолчанию.
|
||||
# Отбрасываем неиспользуемые и перенумеровываем ссылки — индексы шрифтов позиционные.
|
||||
@@ -1953,6 +2036,14 @@ def main():
|
||||
'EndCol': cols[1] - 1 if cols else -1,
|
||||
})
|
||||
|
||||
# 7d-ter. Колонтитулы: шесть слотов идут после строк и перед скалярными свойствами документа.
|
||||
for slot in ('left', 'center', 'right'):
|
||||
if defn.get('header'):
|
||||
emit_header_slot(lines, f'{slot}Header', defn['header'].get(slot), header_fmt)
|
||||
for slot in ('left', 'center', 'right'):
|
||||
if defn.get('footer'):
|
||||
emit_header_slot(lines, f'{slot}Footer', defn['footer'].get(slot), footer_fmt)
|
||||
|
||||
# 7e-bis. Группировки строк и колонок. Платформа хранит их плоским списком диапазонов;
|
||||
# вложенность выражена вхождением одного диапазона в другой, а частичных пересечений не
|
||||
# бывает вовсе (корпус: 40 620 886 пар непересекающихся, 599 958 вложенных, 0 частичных).
|
||||
@@ -2063,6 +2154,8 @@ def main():
|
||||
emit_groups(lines, 'vg', row_groups)
|
||||
emit_groups(lines, 'hg', col_groups)
|
||||
|
||||
emit_print_settings(lines, defn.get('printSettings'))
|
||||
|
||||
# 7f. Merges
|
||||
for m in merges:
|
||||
lines.append('\t<merge>')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# mxl-decompile v1.24 — Decompile 1C spreadsheet to JSON
|
||||
# mxl-decompile v1.25 — Decompile 1C spreadsheet to JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -265,6 +265,68 @@ function Get-Format {
|
||||
return $rawFormats[$idx - 1]
|
||||
}
|
||||
|
||||
# --- 4a. Колонтитулы и параметры печати ---
|
||||
# Слот колонтитула устроен как ячейка: ссылка на формат плюс текст. Признак вывода и стартовую
|
||||
# страницу читаем только у записи, где есть <height>: палитра дедуплицирована, и колонтитул без
|
||||
# своих настроек ссылается на чужую запись, где <width> — ширина колонки, а не номер страницы.
|
||||
function Read-HeaderPart {
|
||||
param([string]$suffix)
|
||||
$out = [ordered]@{}
|
||||
$fmtIdx = $null
|
||||
foreach ($slot in @('left', 'center', 'right')) {
|
||||
$el = $root.SelectSingleNode("d:$slot$suffix", $ns)
|
||||
if (-not $el) { continue }
|
||||
if ($null -eq $fmtIdx) {
|
||||
$fNode = $el.SelectSingleNode("d:f", $ns)
|
||||
$fmtIdx = if ($fNode) { [int]$fNode.InnerText } else { 0 }
|
||||
}
|
||||
foreach ($textTag in @('tl', 'tfl')) {
|
||||
$t = $el.SelectSingleNode("d:$textTag", $ns)
|
||||
if (-not $t) { continue }
|
||||
$byLang = [ordered]@{}
|
||||
foreach ($it in $t.SelectNodes("v8:item", $ns)) {
|
||||
$l = $it.SelectSingleNode("v8:lang", $ns)
|
||||
$cnt = $it.SelectSingleNode("v8:content", $ns)
|
||||
$byLang[$(if ($l) { $l.InnerText } else { '' })] = $(if ($cnt) { $cnt.InnerText } else { '' })
|
||||
}
|
||||
$value = Get-DslText $byLang
|
||||
$out[$slot] = if ($textTag -ceq 'tfl') { [ordered]@{ formatted = $value } } else { $value }
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($out.Count -eq 0 -and $null -eq $fmtIdx) { return $null }
|
||||
$fmt = if ($fmtIdx) { Get-Format $fmtIdx } else { $null }
|
||||
if ($fmt) {
|
||||
$props = $fmt.Props
|
||||
$head = [ordered]@{}
|
||||
if ($fmt.FontIdx -ge 0) { $head["font"] = $fontNames[$fmt.FontIdx] }
|
||||
if ($props.Contains('height')) {
|
||||
if ("$($props['height'])" -ceq '-1') { $head["show"] = $false }
|
||||
$w = if ($props.Contains('width')) { [int]$props['width'] } else { 1 }
|
||||
if ($w -ne 1) { $head["startPage"] = $w }
|
||||
}
|
||||
if ($props.Contains('verticalAlignment')) { $head["verticalAlignment"] = $props['verticalAlignment'] }
|
||||
foreach ($k in $out.Keys) { $head[$k] = $out[$k] }
|
||||
$out = $head
|
||||
}
|
||||
return $out
|
||||
}
|
||||
|
||||
$printSettings = $null
|
||||
$psNode = $root.SelectSingleNode("d:printSettings", $ns)
|
||||
if ($psNode) {
|
||||
$printSettings = [ordered]@{}
|
||||
foreach ($c in $psNode.ChildNodes) {
|
||||
if ($c.NodeType -ne [System.Xml.XmlNodeType]::Element) { continue }
|
||||
$v = $c.InnerText.Trim()
|
||||
$i = 0
|
||||
if ($v -ceq 'true') { $printSettings[$c.get_LocalName()] = $true }
|
||||
elseif ($v -ceq 'false') { $printSettings[$c.get_LocalName()] = $false }
|
||||
elseif ([int]::TryParse($v, [ref]$i)) { $printSettings[$c.get_LocalName()] = $i }
|
||||
else { $printSettings[$c.get_LocalName()] = $v }
|
||||
}
|
||||
}
|
||||
|
||||
# --- 4b. Группировки строк и колонок ---
|
||||
# Платформа хранит их плоским списком диапазонов, родитель раньше детей. Число уровней
|
||||
# (<vgLevels>) не читаем: оно выводится из вложенности — совпало на всех 1797 макетах.
|
||||
@@ -1477,6 +1539,14 @@ foreach ($g in ($rowGroups + $colGroups)) {
|
||||
if ($rowGroups.Count -gt 0) { $result["rowGroups"] = [array]$rowGroups }
|
||||
if ($colGroups.Count -gt 0) { $result["columnGroups"] = [array]$colGroups }
|
||||
|
||||
# Читаем здесь, а не в разделе 4a: тексту колонтитула нужен свод языков макета, который
|
||||
# становится известен только после разбора строк.
|
||||
$headerPart = Read-HeaderPart 'Header'
|
||||
$footerPart = Read-HeaderPart 'Footer'
|
||||
if ($headerPart) { $result["header"] = $headerPart }
|
||||
if ($footerPart) { $result["footer"] = $footerPart }
|
||||
if ($printSettings) { $result["printSettings"] = $printSettings }
|
||||
|
||||
$result["areas"] = [array]$dslAreas
|
||||
|
||||
# Именованные области, не выразимые блоком, — координатами. Тип не пишем: он выводится
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# mxl-decompile v1.24 — Decompile 1C spreadsheet to JSON
|
||||
# mxl-decompile v1.25 — Decompile 1C spreadsheet to JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -431,6 +431,67 @@ def main():
|
||||
return None
|
||||
return raw_formats[idx - 1]
|
||||
|
||||
# --- 4a. Колонтитулы и параметры печати ---
|
||||
# Слот колонтитула устроен как ячейка: ссылка на формат плюс текст. Признак вывода и стартовую
|
||||
# страницу читаем только у записи, где есть <height>: палитра дедуплицирована, и колонтитул без
|
||||
# своих настроек ссылается на чужую запись, где <width> — ширина колонки, а не номер страницы.
|
||||
def read_header_part(suffix):
|
||||
slots = OrderedDict()
|
||||
fmt_idx = None
|
||||
for slot in ('left', 'center', 'right'):
|
||||
el = find(root, f'd:{slot}{suffix}')
|
||||
if el is None:
|
||||
continue
|
||||
if fmt_idx is None:
|
||||
fmt_idx = int_of(find(el, 'd:f'))
|
||||
for text_tag in ('tl', 'tfl'):
|
||||
t = find(el, 'd:' + text_tag)
|
||||
if t is None:
|
||||
continue
|
||||
by_lang = OrderedDict()
|
||||
for it in findall(t, 'v8:item'):
|
||||
lang = text_of(find(it, 'v8:lang')) or ''
|
||||
by_lang[lang] = text_of(find(it, 'v8:content')) or ''
|
||||
value = get_dsl_text(by_lang)
|
||||
slots[slot] = OrderedDict([('formatted', value)]) if text_tag == 'tfl' else value
|
||||
break
|
||||
if not slots and fmt_idx is None:
|
||||
return None
|
||||
fmt = get_format(fmt_idx) if fmt_idx else None
|
||||
if not fmt:
|
||||
return slots
|
||||
head = OrderedDict()
|
||||
if fmt["FontIdx"] >= 0:
|
||||
head["font"] = font_names.get(fmt["FontIdx"], "default")
|
||||
props = fmt["Props"]
|
||||
if 'height' in props:
|
||||
if str(props['height']) == '-1':
|
||||
head["show"] = False
|
||||
width = int(props.get('width', 1))
|
||||
if width != 1:
|
||||
head["startPage"] = width
|
||||
if 'verticalAlignment' in props:
|
||||
head["verticalAlignment"] = props['verticalAlignment']
|
||||
head.update(slots)
|
||||
return head
|
||||
|
||||
print_settings = None
|
||||
ps_node = find(root, 'd:printSettings')
|
||||
if ps_node is not None:
|
||||
print_settings = OrderedDict()
|
||||
for c in ps_node:
|
||||
name = etree.QName(c).localname
|
||||
v = (c.text or '').strip()
|
||||
if v == 'true':
|
||||
print_settings[name] = True
|
||||
elif v == 'false':
|
||||
print_settings[name] = False
|
||||
else:
|
||||
try:
|
||||
print_settings[name] = int(v)
|
||||
except ValueError:
|
||||
print_settings[name] = v
|
||||
|
||||
# --- 4b. Группировки строк и колонок ---
|
||||
# Платформа хранит их плоским списком диапазонов, родитель раньше детей. Число уровней
|
||||
# (<vgLevels>) не читаем: оно выводится из вложенности — совпало на всех 1797 макетах.
|
||||
@@ -1472,6 +1533,17 @@ def main():
|
||||
if col_groups:
|
||||
result["columnGroups"] = col_groups
|
||||
|
||||
# Читаем здесь, а не в разделе 4a: тексту колонтитула нужен свод языков макета, который
|
||||
# становится известен только после разбора строк.
|
||||
header_part = read_header_part('Header')
|
||||
footer_part = read_header_part('Footer')
|
||||
if header_part:
|
||||
result["header"] = header_part
|
||||
if footer_part:
|
||||
result["footer"] = footer_part
|
||||
if print_settings:
|
||||
result["printSettings"] = print_settings
|
||||
|
||||
result["areas"] = dsl_areas
|
||||
|
||||
# Именованные области, не выразимые блоком, — координатами. Тип не пишем: он выводится
|
||||
|
||||
Reference in New Issue
Block a user