Skip to content

Commit d27f4c6

Browse files
committed
rfctr: _Header and _Footer take header/footer type
In preparation for adding even-page and first-page header and footer properties, pass the WD_HEADER_FOOTER_INDEX member identifying the header or footer type (primary, first, even) to _Header and _Footer on construction. This will allow each of these classes to serve for all three header/footer types.
1 parent db7a828 commit d27f4c6

File tree

2 files changed

+51
-42
lines changed

2 files changed

+51
-42
lines changed

docx/section.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def footer(self):
8181
The default footer is used for odd-numbered pages when separate odd/even footers
8282
are enabled. It is used for both odd and even-numbered pages otherwise.
8383
"""
84-
return _Footer(self._sectPr, self._document_part)
84+
return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY)
8585

8686
@property
8787
def footer_distance(self):
@@ -117,7 +117,7 @@ def header(self):
117117
The default header is used for odd-numbered pages when separate odd/even headers
118118
are enabled. It is used for both odd and even-numbered pages otherwise.
119119
"""
120-
return _Header(self._sectPr, self._document_part)
120+
return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY)
121121

122122
@property
123123
def header_distance(self):
@@ -227,9 +227,10 @@ def top_margin(self, value):
227227
class _BaseHeaderFooter(BlockItemContainer):
228228
"""Base class for header and footer classes"""
229229

230-
def __init__(self, sectPr, document_part):
230+
def __init__(self, sectPr, document_part, header_footer_index):
231231
self._sectPr = sectPr
232232
self._document_part = document_part
233+
self._hdrftr_index = header_footer_index
233234

234235
@property
235236
def is_linked_to_previous(self):
@@ -325,24 +326,24 @@ class _Footer(_BaseHeaderFooter):
325326
def _add_definition(self):
326327
"""Return newly-added footer part."""
327328
footer_part, rId = self._document_part.add_footer_part()
328-
self._sectPr.add_footerReference(WD_HEADER_FOOTER.PRIMARY, rId)
329+
self._sectPr.add_footerReference(self._hdrftr_index, rId)
329330
return footer_part
330331

331332
@property
332333
def _definition(self):
333334
"""|FooterPart| object containing content of this footer."""
334-
footerReference = self._sectPr.get_footerReference(WD_HEADER_FOOTER.PRIMARY)
335+
footerReference = self._sectPr.get_footerReference(self._hdrftr_index)
335336
return self._document_part.footer_part(footerReference.rId)
336337

337338
def _drop_definition(self):
338339
"""Remove footer definition (footer part) associated with this section."""
339-
rId = self._sectPr.remove_footerReference(WD_HEADER_FOOTER.PRIMARY)
340+
rId = self._sectPr.remove_footerReference(self._hdrftr_index)
340341
self._document_part.drop_rel(rId)
341342

342343
@property
343344
def _has_definition(self):
344345
"""True if a footer is defined for this section."""
345-
footerReference = self._sectPr.get_footerReference(WD_HEADER_FOOTER.PRIMARY)
346+
footerReference = self._sectPr.get_footerReference(self._hdrftr_index)
346347
return False if footerReference is None else True
347348

348349
@property
@@ -351,7 +352,7 @@ def _prior_headerfooter(self):
351352
preceding_sectPr = self._sectPr.preceding_sectPr
352353
return (
353354
None if preceding_sectPr is None
354-
else _Footer(preceding_sectPr, self._document_part)
355+
else _Footer(preceding_sectPr, self._document_part, self._hdrftr_index)
355356
)
356357

357358

@@ -361,24 +362,24 @@ class _Header(_BaseHeaderFooter):
361362
def _add_definition(self):
362363
"""Return newly-added header part."""
363364
header_part, rId = self._document_part.add_header_part()
364-
self._sectPr.add_headerReference(WD_HEADER_FOOTER.PRIMARY, rId)
365+
self._sectPr.add_headerReference(self._hdrftr_index, rId)
365366
return header_part
366367

367368
@property
368369
def _definition(self):
369370
"""|HeaderPart| object containing content of this header."""
370-
headerReference = self._sectPr.get_headerReference(WD_HEADER_FOOTER.PRIMARY)
371+
headerReference = self._sectPr.get_headerReference(self._hdrftr_index)
371372
return self._document_part.header_part(headerReference.rId)
372373

373374
def _drop_definition(self):
374375
"""Remove header definition associated with this section."""
375-
rId = self._sectPr.remove_headerReference(WD_HEADER_FOOTER.PRIMARY)
376+
rId = self._sectPr.remove_headerReference(self._hdrftr_index)
376377
self._document_part.drop_header_part(rId)
377378

378379
@property
379380
def _has_definition(self):
380381
"""True if a header is explicitly defined for this section."""
381-
headerReference = self._sectPr.get_headerReference(WD_HEADER_FOOTER.PRIMARY)
382+
headerReference = self._sectPr.get_headerReference(self._hdrftr_index)
382383
return False if headerReference is None else True
383384

384385
@property
@@ -387,5 +388,5 @@ def _prior_headerfooter(self):
387388
preceding_sectPr = self._sectPr.preceding_sectPr
388389
return (
389390
None if preceding_sectPr is None
390-
else _Header(preceding_sectPr, self._document_part)
391+
else _Header(preceding_sectPr, self._document_part, self._hdrftr_index)
391392
)

tests/test_section.py

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import pytest
88

9-
from docx.enum.section import WD_ORIENT, WD_SECTION
9+
from docx.enum.section import WD_HEADER_FOOTER, WD_ORIENT, WD_SECTION
1010
from docx.parts.document import DocumentPart
1111
from docx.parts.hdrftr import FooterPart, HeaderPart
1212
from docx.section import _BaseHeaderFooter, _Footer, _Header, Section, Sections
@@ -121,7 +121,9 @@ def it_provides_access_to_its_default_footer(
121121

122122
footer = section.footer
123123

124-
_Footer_.assert_called_once_with(sectPr, document_part_)
124+
_Footer_.assert_called_once_with(
125+
sectPr, document_part_, WD_HEADER_FOOTER.PRIMARY
126+
)
125127
assert footer is footer_
126128

127129
def it_provides_access_to_its_default_header(
@@ -133,7 +135,9 @@ def it_provides_access_to_its_default_header(
133135

134136
header = section.header
135137

136-
_Header_.assert_called_once_with(sectPr, document_part_)
138+
_Header_.assert_called_once_with(
139+
sectPr, document_part_, WD_HEADER_FOOTER.PRIMARY
140+
)
137141
assert header is header_
138142

139143
def it_knows_its_start_type(self, start_type_get_fixture):
@@ -413,7 +417,7 @@ def it_knows_when_its_linked_to_the_previous_header_or_footer(
413417
):
414418
has_definition, expected_value = is_linked_get_fixture
415419
_has_definition_prop_.return_value = has_definition
416-
header = _BaseHeaderFooter(None, None)
420+
header = _BaseHeaderFooter(None, None, None)
417421

418422
is_linked = header.is_linked_to_previous
419423

@@ -428,7 +432,7 @@ def it_can_change_whether_it_is_linked_to_previous_header_or_footer(
428432
):
429433
has_definition, new_value, drop_calls, add_calls = is_linked_set_fixture
430434
_has_definition_prop_.return_value = has_definition
431-
header = _BaseHeaderFooter(None, None)
435+
header = _BaseHeaderFooter(None, None, None)
432436

433437
header.is_linked_to_previous = new_value
434438

@@ -440,7 +444,7 @@ def it_provides_access_to_the_header_or_footer_part_for_BlockItemContainer(
440444
):
441445
# ---this override fulfills part of the BlockItemContainer subclass interface---
442446
_get_or_add_definition_.return_value = header_part_
443-
header = _BaseHeaderFooter(None, None)
447+
header = _BaseHeaderFooter(None, None, None)
444448

445449
header_part = header.part
446450

@@ -453,7 +457,7 @@ def it_provides_access_to_the_hdr_or_ftr_element_to_help(
453457
hdr = element("w:hdr")
454458
_get_or_add_definition_.return_value = header_part_
455459
header_part_.element = hdr
456-
header = _BaseHeaderFooter(None, None)
460+
header = _BaseHeaderFooter(None, None, None)
457461

458462
hdr_elm = header._element
459463

@@ -465,7 +469,7 @@ def it_gets_the_definition_when_it_has_one(
465469
):
466470
_has_definition_prop_.return_value = True
467471
_definition_prop_.return_value = header_part_
468-
header = _BaseHeaderFooter(None, None)
472+
header = _BaseHeaderFooter(None, None, None)
469473

470474
header_part = header._get_or_add_definition()
471475

@@ -481,7 +485,7 @@ def but_it_gets_the_prior_definition_when_it_is_linked(
481485
_has_definition_prop_.return_value = False
482486
_prior_headerfooter_prop_.return_value = prior_headerfooter_
483487
prior_headerfooter_._get_or_add_definition.return_value = header_part_
484-
header = _BaseHeaderFooter(None, None)
488+
header = _BaseHeaderFooter(None, None, None)
485489

486490
header_part = header._get_or_add_definition()
487491

@@ -498,7 +502,7 @@ def and_it_adds_a_definition_when_it_is_linked_and_the_first_section(
498502
_has_definition_prop_.return_value = False
499503
_prior_headerfooter_prop_.return_value = None
500504
_add_definition_.return_value = header_part_
501-
header = _BaseHeaderFooter(None, None)
505+
header = _BaseHeaderFooter(None, None, None)
502506

503507
header_part = header._get_or_add_definition()
504508

@@ -564,7 +568,7 @@ class Describe_Footer(object):
564568
def it_can_add_a_footer_part_to_help(self, document_part_, footer_part_):
565569
sectPr = element("w:sectPr{r:a=b}")
566570
document_part_.add_footer_part.return_value = footer_part_, "rId3"
567-
footer = _Footer(sectPr, document_part_)
571+
footer = _Footer(sectPr, document_part_, WD_HEADER_FOOTER.PRIMARY)
568572

569573
footer_part = footer._add_definition()
570574

@@ -577,18 +581,18 @@ def it_can_add_a_footer_part_to_help(self, document_part_, footer_part_):
577581
def it_provides_access_to_its_footer_part_to_help(
578582
self, document_part_, footer_part_
579583
):
580-
sectPr = element("w:sectPr/w:footerReference{w:type=default,r:id=rId3}")
584+
sectPr = element("w:sectPr/w:footerReference{w:type=even,r:id=rId3}")
581585
document_part_.footer_part.return_value = footer_part_
582-
footer = _Footer(sectPr, document_part_)
586+
footer = _Footer(sectPr, document_part_, WD_HEADER_FOOTER.EVEN_PAGE)
583587

584588
footer_part = footer._definition
585589

586590
document_part_.footer_part.assert_called_once_with("rId3")
587591
assert footer_part is footer_part_
588592

589593
def it_can_drop_the_related_footer_part_to_help(self, document_part_):
590-
sectPr = element("w:sectPr{r:a=b}/w:footerReference{w:type=default,r:id=rId42}")
591-
footer = _Footer(sectPr, document_part_)
594+
sectPr = element("w:sectPr{r:a=b}/w:footerReference{w:type=first,r:id=rId42}")
595+
footer = _Footer(sectPr, document_part_, WD_HEADER_FOOTER.FIRST_PAGE)
592596

593597
footer._drop_definition()
594598

@@ -597,7 +601,7 @@ def it_can_drop_the_related_footer_part_to_help(self, document_part_):
597601

598602
def it_knows_when_it_has_a_definition_to_help(self, has_definition_fixture):
599603
sectPr, expected_value = has_definition_fixture
600-
footer = _Footer(sectPr, None)
604+
footer = _Footer(sectPr, None, WD_HEADER_FOOTER.PRIMARY)
601605

602606
has_definition = footer._has_definition
603607

@@ -608,19 +612,21 @@ def it_provides_access_to_the_prior_Footer_to_help(
608612
):
609613
doc_elm = element("w:document/(w:sectPr,w:sectPr)")
610614
prior_sectPr, sectPr = doc_elm[0], doc_elm[1]
611-
footer = _Footer(sectPr, document_part_)
615+
footer = _Footer(sectPr, document_part_, WD_HEADER_FOOTER.EVEN_PAGE)
612616
# ---mock must occur after construction of "real" footer---
613617
_Footer_ = class_mock(request, "docx.section._Footer", return_value=footer_)
614618

615619
prior_footer = footer._prior_headerfooter
616620

617-
_Footer_.assert_called_once_with(prior_sectPr, document_part_)
621+
_Footer_.assert_called_once_with(
622+
prior_sectPr, document_part_, WD_HEADER_FOOTER.EVEN_PAGE
623+
)
618624
assert prior_footer is footer_
619625

620626
def but_it_returns_None_when_its_the_first_footer(self):
621627
doc_elm = element("w:document/w:sectPr")
622628
sectPr = doc_elm[0]
623-
footer = _Footer(sectPr, None)
629+
footer = _Footer(sectPr, None, None)
624630

625631
prior_footer = footer._prior_headerfooter
626632

@@ -658,13 +664,13 @@ class Describe_Header(object):
658664
def it_can_add_a_header_part_to_help(self, document_part_, header_part_):
659665
sectPr = element("w:sectPr{r:a=b}")
660666
document_part_.add_header_part.return_value = header_part_, "rId3"
661-
header = _Header(sectPr, document_part_)
667+
header = _Header(sectPr, document_part_, WD_HEADER_FOOTER.FIRST_PAGE)
662668

663669
header_part = header._add_definition()
664670

665671
document_part_.add_header_part.assert_called_once_with()
666672
assert sectPr.xml == xml(
667-
"w:sectPr{r:a=b}/w:headerReference{w:type=default,r:id=rId3}"
673+
"w:sectPr{r:a=b}/w:headerReference{w:type=first,r:id=rId3}"
668674
)
669675
assert header_part is header_part_
670676

@@ -673,16 +679,16 @@ def it_provides_access_to_its_header_part_to_help(
673679
):
674680
sectPr = element("w:sectPr/w:headerReference{w:type=default,r:id=rId8}")
675681
document_part_.header_part.return_value = header_part_
676-
header = _Header(sectPr, document_part_)
682+
header = _Header(sectPr, document_part_, WD_HEADER_FOOTER.PRIMARY)
677683

678684
header_part = header._definition
679685

680686
document_part_.header_part.assert_called_once_with("rId8")
681687
assert header_part is header_part_
682688

683689
def it_can_drop_the_related_header_part_to_help(self, document_part_):
684-
sectPr = element("w:sectPr{r:a=b}/w:headerReference{w:type=default,r:id=rId42}")
685-
header = _Header(sectPr, document_part_)
690+
sectPr = element("w:sectPr{r:a=b}/w:headerReference{w:type=even,r:id=rId42}")
691+
header = _Header(sectPr, document_part_, WD_HEADER_FOOTER.EVEN_PAGE)
686692

687693
header._drop_definition()
688694

@@ -691,7 +697,7 @@ def it_can_drop_the_related_header_part_to_help(self, document_part_):
691697

692698
def it_knows_when_it_has_a_header_part_to_help(self, has_definition_fixture):
693699
sectPr, expected_value = has_definition_fixture
694-
header = _Header(sectPr, None)
700+
header = _Header(sectPr, None, WD_HEADER_FOOTER.FIRST_PAGE)
695701

696702
has_definition = header._has_definition
697703

@@ -702,19 +708,21 @@ def it_provides_access_to_the_prior_Header_to_help(
702708
):
703709
doc_elm = element("w:document/(w:sectPr,w:sectPr)")
704710
prior_sectPr, sectPr = doc_elm[0], doc_elm[1]
705-
header = _Header(sectPr, document_part_)
711+
header = _Header(sectPr, document_part_, WD_HEADER_FOOTER.PRIMARY)
706712
# ---mock must occur after construction of "real" header---
707713
_Header_ = class_mock(request, "docx.section._Header", return_value=header_)
708714

709715
prior_header = header._prior_headerfooter
710716

711-
_Header_.assert_called_once_with(prior_sectPr, document_part_)
717+
_Header_.assert_called_once_with(
718+
prior_sectPr, document_part_, WD_HEADER_FOOTER.PRIMARY
719+
)
712720
assert prior_header is header_
713721

714722
def but_it_returns_None_when_its_the_first_header(self):
715723
doc_elm = element("w:document/w:sectPr")
716724
sectPr = doc_elm[0]
717-
header = _Header(sectPr, None)
725+
header = _Header(sectPr, None, None)
718726

719727
prior_header = header._prior_headerfooter
720728

@@ -724,7 +732,7 @@ def but_it_returns_None_when_its_the_first_header(self):
724732

725733
@pytest.fixture(
726734
params=[
727-
("w:sectPr", False), ("w:sectPr/w:headerReference{w:type=default}", True)
735+
("w:sectPr", False), ("w:sectPr/w:headerReference{w:type=first}", True)
728736
]
729737
)
730738
def has_definition_fixture(self, request):

0 commit comments

Comments
 (0)