fix: Word разбивает маркеры циклов/переменные на run'ы + адрес ИС
- _normalize_loop_markers: склейка текста параграфа в один run перед
поиском <!-- loop:... --> и {{item.*}}, иначе циклы не разворачивались
(шаблон '6. Акт определения уровня защищенности ПДн.docx')
- variables.py: поле address в цикле information_systems
This commit is contained in:
@@ -68,6 +68,59 @@ OLD_VAR_MAP = {
|
||||
# 2. ОБРАБОТКА ЦИКЛОВ НА УРОВНЕ ZIP/XML
|
||||
# ============================================================
|
||||
|
||||
W_NS = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
|
||||
|
||||
|
||||
def _normalize_loop_markers(doc_xml: str) -> str:
|
||||
"""Word разбивает маркеры <!-- loop:... --> и переменные {{item.x}}
|
||||
на несколько run'ов (например '<!-- ' в одном <w:t>, 'loop:info' в другом,
|
||||
'rmation -->' в третьем). Склеиваем текст каждого параграфа в один run,
|
||||
чтобы маркеры и переменные циклов снова стали едиными."""
|
||||
try:
|
||||
root = etree.fromstring(doc_xml.encode('utf-8'))
|
||||
except Exception:
|
||||
return doc_xml
|
||||
|
||||
changed = False
|
||||
for p in root.iter(f'{{{W_NS}}}p'):
|
||||
# Собираем весь текст параграфа (по всем run'ам)
|
||||
texts = []
|
||||
for t in p.iter(f'{{{W_NS}}}t'):
|
||||
texts.append(t.text or '')
|
||||
full = ''.join(texts)
|
||||
|
||||
# Интересуют только параграфы с маркерами циклов ИЛИ переменными цикла
|
||||
if 'loop:' not in full and 'loop_end' not in full and '{{item.' not in full:
|
||||
continue
|
||||
|
||||
# Сохраняем форматирование первого run
|
||||
first_r = p.find(f'{{{W_NS}}}r')
|
||||
rpr = None
|
||||
if first_r is not None:
|
||||
rpr_el = first_r.find(f'{{{W_NS}}}rPr')
|
||||
if rpr_el is not None:
|
||||
rpr = etree.fromstring(etree.tostring(rpr_el))
|
||||
|
||||
# Оставляем только pPr, удаляем все run'ы
|
||||
for child in list(p):
|
||||
if child.tag != f'{{{W_NS}}}pPr':
|
||||
p.remove(child)
|
||||
|
||||
# Создаём один run с полным текстом параграфа
|
||||
new_r = etree.SubElement(p, f'{{{W_NS}}}r')
|
||||
if rpr is not None:
|
||||
new_r.append(rpr)
|
||||
new_t = etree.SubElement(new_r, f'{{{W_NS}}}t')
|
||||
new_t.text = full
|
||||
new_t.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
|
||||
changed = True
|
||||
|
||||
if not changed:
|
||||
return doc_xml
|
||||
|
||||
return etree.tostring(root, xml_declaration=True, encoding='UTF-8', standalone=True).decode('utf-8')
|
||||
|
||||
|
||||
def expand_loops_in_zip(docx_path: str, loops: Dict[str, List[Dict]]) -> str:
|
||||
"""Обрабатывает циклы напрямую в ZIP/DOCX на уровне XML.
|
||||
Работает до загрузки python-docx — гарантирует корректную вложенность.
|
||||
@@ -102,6 +155,9 @@ def expand_loops_in_zip(docx_path: str, loops: Dict[str, List[Dict]]) -> str:
|
||||
if not doc_xml:
|
||||
break
|
||||
|
||||
# Word мог разбить маркеры циклов на несколько run'ов — склеиваем
|
||||
doc_xml = _normalize_loop_markers(doc_xml)
|
||||
|
||||
# Находим ВСЕ маркеры циклов
|
||||
start_markers = list(LOOP_START.finditer(doc_xml))
|
||||
end_markers = list(LOOP_END.finditer(doc_xml))
|
||||
|
||||
Reference in New Issue
Block a user