148 lines
4.3 KiB
Python
148 lines
4.3 KiB
Python
"""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
|