8000 doc: Document.add_section() strips hdr/ftr refs · python-openxml/python-docx@5a31034 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5a31034

Browse files
committed
doc: Document.add_section() strips hdr/ftr refs
The implementation of `Document.add_section()` "clones" the previously last `w:sectPr` element to produce the `w:sectPr` for the new section. This has the effect of duplicating the header and footer references along with the rIds used to link them to their header or footer part. There have been no reports that this causes a problem, but in any case the correct behavior is to add a new section without any header or footer definitions, such that the new section "inherits" all header/footer definitions (and header/footer parts are not "reused"). Add code to remove all `w:headerReference` and `w:footerReference` elements from a "cloned" `w:sectPr` element when adding a section.
1 parent a2df1d1 commit 5a31034

File tree

5 files changed

+47
-9
lines changed

5 files changed

+47
-9
lines changed

docx/oxml/document.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,25 @@ class CT_Body(BaseOxmlElement):
3333
sectPr = ZeroOrOne('w:sectPr', successors=())
3434

3535
def add_section_break(self):
36+
"""Return `w:sectPr` element for new section added at end of document.
37+
38+
The last `w:sectPr` becomes the second-to-last, with the new `w:sectPr` being an
39+
exact clone of the previous one, except that all header and footer references
40+
are removed (and are therefore now "inherited" from the prior section).
41+
42+
A copy of the previously-last `w:sectPr` will now appear in a new `w:p` at the
43+
end of the document. The returned `w:sectPr` is the sentinel `w:sectPr` for the
44+
document (and as implemented, *is* the prior sentinel `w:sectPr` with headers
45+
and footers removed).
3646
"""
37-
Return the current ``<w:sectPr>`` element after adding a clone of it
38-
in a new ``<w:p>`` element appended to the block content elements.
39-
Note that the "current" ``<w:sectPr>`` will always be the sentinel
40-
sectPr in this case since we're always working at the end of the
41-
block content.
42-
"""
47+
# ---get the sectPr at file-end, which controls last section (sections[-1])---
4348
sentinel_sectPr = self.get_or_add_sectPr()
44-
cloned_sectPr = sentinel_sectPr.clone()
45-
p = self.add_p()
46-
p.set_sectPr(cloned_sectPr)
49+
# ---add exact copy to new `w:p` element; that is now second-to last section---
50+
self.add_p().set_sectPr(sentinel_sectPr.clone())
51+
# ---remove any header or footer references from "new" last section---
52+
for hdrftr_ref in sentinel_sectPr.xpath("w:headerReference|w:footerReference"):
53+
sentinel_sectPr.remove(hdrftr_ref)
54+
# ---the sentinel `w:sectPr` now controls the new last section---
4755
return sentinel_sectPr
4856

4957
def clear_content(self):

features/doc-add-section.feature

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,14 @@ Feature: Add a document section
1111
Then the document has two sections
1212
And the first section is portrait
1313
And the second section is landscape
14+
15+
16+
Scenario: Document.add_section() adds a section that inherits headers and footers
17+
Given a single-section Document object with headers and footers as document
18+
When I execute section = document.add_section()
19+
Then section.header.is_linked_to_previous is True
20+
And section.even_page_header.is_linked_to_previous is True
21+
And section.first_page_header.is_linked_to_previous is True
22+
And section.footer.is_linked_to_previous is True
23+
And section.even_page_footer.is_linked_to_previous is True
24+
And section.first_page_footer.is_linked_to_previous is True

features/steps/document.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ def given_a_single_section_document_having_portrait_layout(context):
5959
context.original_dimensions = (section.page_width, section.page_height)
6060

6161

62+
@given("a single-section Document object with headers and footers as document")
63+
def given_a_single_section_Document_object_with_headers_and_footers(context):
64+
context.document = Document(test_docx("doc-add-section"))
65+
66+
6267
# when ====================================================
6368

6469
@when('I add a 2 x 2 table specifying only row and column count')
@@ -160,6 +165,11 @@ def when_I_change_the_new_section_layout_to_landscape(context):
160165
section.page_height = new_height
161166

162167

168+
@when("I execute section = document.add_section()")
169+
def when_I_execute_section_eq_document_add_section(context):
170+
context.section = context.document.add_section()
171+
172+
163173
# then ====================================================
164174

165175
@then('document.inline_shapes is an InlineShapes object')

features/steps/section.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ def then_section_header_is_a_Header_object(context):
204204
assert actual == expected, "section.header is a %s object" % actual
205205

206206

207+
@then("section.{propname}.is_linked_to_previous is True")
208+
def then_section_hdrftr_prop_is_linked_to_previous_is_True(context, propname):
209+
actual = getattr(context.section, propname).is_linked_to_previous
210+
expected = True
211+
assert actual == expected, (
212+
"section.%s.is_linked_to_previous is %s" % (propname, actual)
213+
)
214+
215+
207216
@then('the reported {margin_side} margin is {inches} inches')
208217
def then_the_reported_margin_is_inches(context, margin_side, inches):
209218
prop_name = {
-3.23 KB
Binary file not shown.

0 commit comments

Comments
 (0)
0