Skip to content

Commit 89d7493

Browse files
eupharisSteve Canny
authored andcommitted
hdr: add _BaseHeaderFooter.body
1 parent 3c7ec69 commit 89d7493

File tree

5 files changed

+83
-9
lines changed

5 files changed

+83
-9
lines changed

docx/header.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
absolute_import, division, print_function, unicode_literals
99
)
1010

11-
from .shared import ElementProxy
11+
from .shared import ElementProxy, lazyproperty
1212

1313

1414
class _BaseHeaderFooter(ElementProxy):
@@ -23,6 +23,16 @@ def __init__(self, element, parent, type):
2323
self._sectPr = element
2424
self._type = type
2525

26+
@lazyproperty
27+
def body(self):
28+
"""
29+
BlockItemContainer instance with contents of Header
30+
"""
31+
headerReference = self._sectPr.get_headerReference_of_type(self._type)
32+
if headerReference is None:
33+
return None
34+
return self.part.related_hdrftr_body(headerReference.rId)
35+
2636
@property
2737
def is_linked_to_previous(self):
2838
"""
@@ -39,3 +49,10 @@ class Header(_BaseHeaderFooter):
3949
"""
4050
One of the page headers for a section.
4151
"""
52+
53+
54+
class HeaderFooterBody(object):
55+
"""
56+
The rich-text body of a header or footer. Supports the same rich text
57+
operations as a document, such as paragraphs and tables.
58+
"""

docx/oxml/__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,14 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
8686
register_element_cls('w:numbering', CT_Numbering)
8787
register_element_cls('w:startOverride', CT_DecimalNumber)
8888

89-
from .section import CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType
90-
register_element_cls('w:pgMar', CT_PageMar)
91-
register_element_cls('w:pgSz', CT_PageSz)
92-
register_element_cls('w:sectPr', CT_SectPr)
93-
register_element_cls('w:type', CT_SectType)
89+
from .section import (
90+
CT_HdrFtrRef, CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType
91+
)
92+
register_element_cls('w:headerReference', CT_HdrFtrRef)
93+
register_element_cls('w:pgMar', CT_PageMar)
94+
register_element_cls('w:pgSz', CT_PageSz)
95+
register_element_cls('w:sectPr', CT_SectPr)
96+
register_element_cls('w:type', CT_SectType)
9497

9598
from .shape import (
9699
CT_Blip, CT_BlipFillProperties, CT_GraphicalObject,

docx/oxml/section.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,23 @@
1010

1111
from ..enum.header import WD_HEADER_FOOTER
1212
from ..enum.section import WD_ORIENTATION, WD_SECTION_START
13-
from .simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure
13+
from .simpletypes import (
14+
ST_RelationshipId, ST_SignedTwipsMeasure, ST_TwipsMeasure
15+
)
1416
from .xmlchemy import (
15-
BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne
17+
BaseOxmlElement, OptionalAttribute, RequiredAttribute, ZeroOrMore,
18+
ZeroOrOne
1619
)
1720

1821

22+
class CT_HdrFtrRef(BaseOxmlElement):
23+
"""
24+
`w:headerReference` and `w:footerReference` elements, specifying the
25+
various headers and footers for a section.
26+
"""
27+
rId = RequiredAttribute('r:id', ST_RelationshipId)
28+
29+
1930
class CT_PageMar(BaseOxmlElement):
2031
"""
2132
``<w:pgMar>`` element, defining page margins.

docx/parts/document.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ def numbering_part(self):
121121
self.relate_to(numbering_part, RT.NUMBERING)
122122
return numbering_part
123123

124+
def related_hdrftr_body(self, rId):
125+
"""
126+
Return the |HeaderFooterBody| object corresponding to the related
127+
part identified by *rId*.
128+
"""
129+
raise NotImplementedError
130+
124131
def save(self, path_or_stream):
125132
"""
126133
Save this document to *path_or_stream*, which can be either a path to

tests/test_header.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import pytest
1212

1313
from docx.enum.header import WD_HEADER_FOOTER
14-
from docx.header import Header
14+
from docx.header import _BaseHeaderFooter, Header, HeaderFooterBody
15+
from docx.parts.document import DocumentPart
1516

1617
from .unitutil.cxml import element
18+
from .unitutil.mock import call, instance_mock, property_mock
1719

1820

1921
class Describe_BaseHeaderFooter(object):
@@ -22,8 +24,26 @@ def it_knows_whether_it_is_linked_to_previous(self, is_linked_fixture):
2224
header, expected_value = is_linked_fixture
2325
assert header.is_linked_to_previous is expected_value
2426

27+
def it_provides_access_to_its_body(self, body_fixture):
28+
header, calls, expected_value = body_fixture
29+
body = header.body
30+
assert header.part.related_hdrftr_body.call_args_list == calls
31+
assert body == expected_value
32+
2533
# fixtures -------------------------------------------------------
2634

35+
@pytest.fixture(params=[
36+
('w:sectPr', None),
37+
('w:sectPr/w:headerReference{w:type=even,r:id=rId6}', None),
38+
('w:sectPr/w:headerReference{w:type=default,r:id=rId8}', 'rId8'),
39+
])
40+
def body_fixture(self, request, body_, part_prop_, document_part_):
41+
sectPr_cxml, rId = request.param
42+
header = Header(element(sectPr_cxml), None, WD_HEADER_FOOTER.PRIMARY)
43+
calls, expected_value = ([call(rId)], body_) if rId else ([], None)
44+
document_part_.related_hdrftr_body.return_value = body_
45+
return header, calls, expected_value
46+
2747
@pytest.fixture(params=[
2848
('w:sectPr', True),
2949
('w:sectPr/w:headerReference{w:type=default}', False),
@@ -33,3 +53,19 @@ def is_linked_fixture(self, request):
3353
sectPr_cxml, expected_value = request.param
3454
header = Header(element(sectPr_cxml), None, WD_HEADER_FOOTER.PRIMARY)
3555
return header, expected_value
56+
57+
# fixture components ---------------------------------------------
58+
59+
@pytest.fixture
60+
def body_(self, request):
61+
return instance_mock(request, HeaderFooterBody)
62+
63+
@pytest.fixture
64+
def document_part_(self, request):
65+
return instance_mock(request, DocumentPart)
66+
67+
@pytest.fixture
68+
def part_prop_(self, request, document_part_):
69+
return property_mock(
70+
request, _BaseHeaderFooter, 'part', return_value=document_part_
71+
)

0 commit comments

Comments
 (0)