Skip to content

Commit ac8185f

Browse files
fix: trailing blank page (#78)
* fix: trailing section * version bump
1 parent 4bbc8e7 commit ac8185f

4 files changed

Lines changed: 24 additions & 20 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cmi-docx"
3-
version = "0.6.9"
3+
version = "0.6.10"
44
description = "Additional tooling for Python-docx."
55
readme = "README.md"
66
requires-python = ">=3.12"

src/cmi_docx/declarative/document.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,18 @@ async def to_docx( # noqa: C901
143143

144144
paragraph_index = template.paragraph_index if template is not None else None
145145
insertion_offset = 0
146-
for sec in self.sections:
146+
for i, sec in enumerate(self.sections):
147147
current_index = (
148148
(paragraph_index + insertion_offset)
149149
if paragraph_index is not None
150150
else None
151151
)
152152
elements_inserted = _pack_section(
153-
docx_doc, sec, self.comment_author, current_index
153+
docx_doc,
154+
sec,
155+
self.comment_author,
156+
current_index,
157+
is_last=(i == len(self.sections) - 1),
154158
)
155159
insertion_offset += elements_inserted
156160

@@ -430,6 +434,8 @@ def _pack_section( # noqa: C901, PLR0912
430434
sec: section.Section,
431435
default_comment_author: str | None,
432436
paragraph_index: int | None = None,
437+
*,
438+
is_last: bool = False,
433439
) -> int:
434440
"""Pack a Section into a python-docx document.
435441
@@ -439,6 +445,8 @@ def _pack_section( # noqa: C901, PLR0912
439445
default_comment_author: Default author for comments.
440446
paragraph_index: If provided, insert children starting at this paragraph
441447
index instead of appending.
448+
is_last: If True, skip adding a new section at the end (avoids a
449+
trailing blank page after the final section).
442450
443451
Returns:
444452
The number of block elements inserted (for offset tracking).
@@ -522,7 +530,8 @@ def _pack_section( # noqa: C901, PLR0912
522530
docx_doc, current_section, footer_type, footer, default_comment_author
523531
)
524532

525-
docx_doc.add_section()
533+
if not is_last:
534+
docx_doc.add_section()
526535

527536
return paragraphs_inserted
528537

tests/declarative/test_section_margins.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async def test_all_margins() -> None:
3030
)
3131

3232
docx_doc = await doc.to_docx()
33-
section = docx_doc.sections[-2]
33+
section = docx_doc.sections[-1]
3434
assert section.top_margin == Inches(2)
3535
assert section.bottom_margin == Inches(2)
3636
assert section.left_margin == Inches(2)
@@ -60,7 +60,7 @@ async def test_partial_margins() -> None:
6060
)
6161

6262
docx_doc = await doc.to_docx()
63-
section = docx_doc.sections[-2]
63+
section = docx_doc.sections[-1]
6464
assert section.top_margin == Inches(1.5)
6565
assert section.left_margin == Inches(1.5)
6666
assert section.bottom_margin != Inches(1.5)
@@ -84,7 +84,7 @@ async def test_no_margins() -> None:
8484
)
8585

8686
docx_doc = await doc.to_docx()
87-
section = docx_doc.sections[-2]
87+
section = docx_doc.sections[-1]
8888
assert section.top_margin is not None
8989
assert section.bottom_margin is not None
9090
assert section.left_margin is not None

tests/declarative/test_section_properties.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async def test_landscape_no_page_size() -> None:
2525
)
2626

2727
docx_doc = await doc.to_docx()
28-
section = docx_doc.sections[-2]
28+
section = docx_doc.sections[-1]
2929
assert section.page_width is not None
3030
assert section.page_height is not None
3131
assert section.page_width > section.page_height
@@ -52,7 +52,7 @@ async def test_landscape_with_page_size() -> None:
5252
)
5353

5454
docx_doc = await doc.to_docx()
55-
section = docx_doc.sections[-2]
55+
section = docx_doc.sections[-1]
5656
assert section.page_width is not None
5757
assert section.page_height is not None
5858
assert section.page_width > section.page_height
@@ -77,7 +77,7 @@ async def test_portrait_no_page_size() -> None:
7777
)
7878

7979
docx_doc = await doc.to_docx()
80-
section = docx_doc.sections[-2]
80+
section = docx_doc.sections[-1]
8181
assert section.page_width is not None
8282
assert section.page_height is not None
8383
assert section.page_width < section.page_height
@@ -99,7 +99,7 @@ async def test_no_orientation() -> None:
9999
)
100100

101101
docx_doc = await doc.to_docx()
102-
section = docx_doc.sections[-2]
102+
section = docx_doc.sections[-1]
103103
assert section.page_width is not None
104104
assert section.page_height is not None
105105

@@ -109,13 +109,8 @@ async def test_landscape_multi_section() -> None:
109109
"""Test that landscape properties apply to the correct section index.
110110
111111
With 2 declarative sections (portrait then landscape), the resulting
112-
docx_doc.sections list has 3 entries: sections[0] is portrait, sections[1]
113-
is landscape, and sections[2] is the trailing empty section added by the
114-
final add_section() call.
115-
116-
This test would have failed with the old off-by-one bug, where landscape
117-
properties were applied to sections[2] (the trailing sentinel) instead of
118-
sections[1] (the second declarative section).
112+
docx_doc.sections list has exactly 2 entries: sections[0] is portrait
113+
and sections[1] is landscape.
119114
"""
120115
doc = declarative.Document(
121116
sections=[
@@ -137,8 +132,8 @@ async def test_landscape_multi_section() -> None:
137132
docx_doc = await doc.to_docx()
138133
sections = list(docx_doc.sections)
139134

140-
# 2 declarative sections produce 3 total sections (trailing sentinel at [-1]).
141-
expected_section_count = 3
135+
# 2 declarative sections produce exactly 2 sections.
136+
expected_section_count = 2
142137
assert len(sections) == expected_section_count
143138

144139
portrait_section = sections[0]

0 commit comments

Comments
 (0)