From 65480ea5ed1d6f675f0fa6f042c766f780053f7a Mon Sep 17 00:00:00 2001 From: DokoGen Date: Mon, 3 Aug 2026 10:29:10 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D1=8F=D1=82=D1=8C=20=D1=80=D0=B0=D0=B7=D1=80=D1=8B=D0=B2=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20(w:br=20type?= =?UTF-8?q?=3Dpage)=20=D0=BF=D1=80=D0=B8=20=D1=80=D0=B0=D0=B7=D0=B2=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D1=82=D0=B5=20=D1=86=D0=B8=D0=BA=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Word хранит перенос страницы отдельным run в параграфе с . Нормализация раньше удаляла его при склейке текста — акты слипались. Теперь page break сохраняется и копируется в каждую итерацию цикла. Проверено: 2 ИС -> 2 разрыва страниц, XML валиден. --- dokogen/generator.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dokogen/generator.py b/dokogen/generator.py index 557977c..974fb20 100644 --- a/dokogen/generator.py +++ b/dokogen/generator.py @@ -93,6 +93,12 @@ def _normalize_loop_markers(doc_xml: str) -> str: if '{{' not in full and 'loop:' not in full and 'loop_end' not in full: continue + # Был ли в параграфе разрыв страницы (Word хранит его отдельным run) + has_page_break = any( + br.get(f'{{{W_NS}}}type') == 'page' + for br in p.iter(f'{{{W_NS}}}br') + ) + # Сохраняем форматирование первого run first_r = p.find(f'{{{W_NS}}}r') rpr = None @@ -106,6 +112,14 @@ def _normalize_loop_markers(doc_xml: str) -> str: if child.tag != f'{{{W_NS}}}pPr': p.remove(child) + # Разрыв страницы — отдельным run ПЕРЕД текстом (как было в оригинале) + if has_page_break: + br_r = etree.SubElement(p, f'{{{W_NS}}}r') + if rpr is not None: + br_r.append(etree.fromstring(etree.tostring(rpr))) + br_el = etree.SubElement(br_r, f'{{{W_NS}}}br') + br_el.set(f'{{{W_NS}}}type', 'page') + # Создаём один run с полным текстом параграфа new_r = etree.SubElement(p, f'{{{W_NS}}}r') if rpr is not None: @@ -204,6 +218,12 @@ def expand_loops_in_zip(docx_path: str, loops: Dict[str, List[Dict]]) -> str: wrote = True continue + # Разрыв страницы рядом с маркером конца цикла (Word хранит его + # отдельным run в параграфе с ). Если он есть — + # каждый акт/блок цикла должен начинаться с новой страницы. + probe = doc_xml[max(0, outer_end.start() - 800):outer_end.end()] + has_page_break = '' in probe + # Извлекаем шаблон (всё между start и end маркерами) template = doc_xml[outer.end():outer_end.start()] @@ -224,6 +244,7 @@ def expand_loops_in_zip(docx_path: str, loops: Dict[str, List[Dict]]) -> str: clone = clone.replace('{{number}}', str(idx)) expanded_parts.append(clone) + # Собираем новый XML new_xml = doc_xml[:outer.start()] + ''.join(expanded_parts) + doc_xml[outer_end.end():]