"""Page number start / offset helpers.""" from pathlib import Path from zipfile import ZipFile from docx import Document from docx.oxml.ns import qn from lxml import etree from md2gost.page_geometry import ( apply_page_number_start, apply_section_geometry, clear_section_page_start, patch_docx_page_starts, set_section_page_start, ) _W = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}" def _pg_start(section): pg = section._sectPr.find(qn("w:pgNumType")) if pg is None: return None return pg.get(qn("w:start")) def test_set_section_page_start(): doc = Document() section = doc.sections[0] set_section_page_start(section, 3) pg = section._sectPr.find(qn("w:pgNumType")) assert pg is not None assert pg.get(qn("w:start")) == "3" def test_apply_page_number_start_skips_front(): doc = Document() doc.add_section() # front doc.add_section() # body apply_page_number_start(doc, 2, front_sections=1) front = doc.sections[0]._sectPr.find(qn("w:pgNumType")) body = doc.sections[1]._sectPr.find(qn("w:pgNumType")) assert front is None or front.get(qn("w:start")) is None assert body is not None assert body.get(qn("w:start")) == "2" def test_apply_page_number_start_none_strips(): doc = Document() set_section_page_start(doc.sections[0], 5) apply_page_number_start(doc, None) pg = doc.sections[0]._sectPr.find(qn("w:pgNumType")) assert pg is None or pg.get(qn("w:start")) is None def test_apply_page_number_start_none_strips_all_four_sections(): doc = Document() for _ in range(3): doc.add_section() for section in doc.sections: set_section_page_start(section, 1) assert all(_pg_start(s) == "1" for s in doc.sections) apply_page_number_start(doc, None) assert all(_pg_start(s) is None for s in doc.sections) def test_apply_page_number_start_body_only_with_front(): doc = Document() for _ in range(3): doc.add_section() # front + body + landscape + portrait = 4 for section in doc.sections: set_section_page_start(section, 1) apply_page_number_start(doc, 1, front_sections=1) assert _pg_start(doc.sections[0]) is None assert _pg_start(doc.sections[1]) == "1" assert _pg_start(doc.sections[2]) is None assert _pg_start(doc.sections[3]) is None def test_apply_section_geometry_strips_copied_start(): doc = Document() set_section_page_start(doc.sections[0], 1) apply_section_geometry(doc.sections[0], landscape=True) assert _pg_start(doc.sections[0]) is None def test_clear_section_page_start_removes_empty_element(): doc = Document() set_section_page_start(doc.sections[0], 1) clear_section_page_start(doc.sections[0]) assert doc.sections[0]._sectPr.find(qn("w:pgNumType")) is None def test_patch_docx_page_starts_strips_and_restores(tmp_path: Path): doc = Document() for _ in range(3): doc.add_section() for section in doc.sections: set_section_page_start(section, 1) path = tmp_path / "pages.docx" doc.save(path) patch_docx_page_starts(path, None) with ZipFile(path) as z: root = etree.fromstring(z.read("word/document.xml")) starts = [ el.get(f"{_W}start") for el in root.findall(f".//{_W}pgNumType") ] assert all(s is None for s in starts) # Re-seed starts then restore body-only restart. doc2 = Document(str(path)) for section in doc2.sections: set_section_page_start(section, 1) doc2.save(path) patch_docx_page_starts(path, 2, front_sections=1) with ZipFile(path) as z: root = etree.fromstring(z.read("word/document.xml")) body = root.find(f"{_W}body") sects = [] for child in body: if child.tag == f"{_W}sectPr": sects.append(child) continue if child.tag != f"{_W}p": continue p_pr = child.find(f"{_W}pPr") if p_pr is None: continue sect = p_pr.find(f"{_W}sectPr") if sect is not None: sects.append(sect) assert len(sects) == 4 vals = [ (s.find(f"{_W}pgNumType").get(f"{_W}start") if s.find(f"{_W}pgNumType") is not None else None) for s in sects ] assert vals[0] is None assert vals[1] == "2" assert vals[2] is None assert vals[3] is None