Skip to content

Commit 383ebd1

Browse files
ondrej-111scanny
authored andcommitted
hdr: add _Header._drop_header_part()
1 parent e573716 commit 383ebd1

File tree

7 files changed

+83
-26
lines changed

7 files changed

+83
-26
lines changed

docx/oxml/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
8484
register_element_cls('w:numbering', CT_Numbering)
8585
register_element_cls('w:startOverride', CT_DecimalNumber)
8686

87-
from .section import CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType # noqa
87+
from .section import CT_HdrFtrRef, CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType # noqa
88+
register_element_cls('w:headerReference', CT_HdrFtrRef)
8889
register_element_cls('w:pgMar', CT_PageMar)
8990
register_element_cls('w:pgSz', CT_PageSz)
9091
register_element_cls('w:sectPr', CT_SectPr)

docx/oxml/section.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,22 @@
77
from copy import deepcopy
88

99
from docx.enum.section import WD_HEADER_FOOTER, WD_ORIENTATION, WD_SECTION_START
10-
from docx.oxml.simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure
10+
from docx.oxml.simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure, XsdString
1111
from docx.oxml.xmlchemy import (
1212
BaseOxmlElement,
1313
OptionalAttribute,
14+
RequiredAttribute,
1415
ZeroOrOne,
1516
)
1617

1718

19+
class CT_HdrFtrRef(BaseOxmlElement):
20+
"""`w:headerReference` and `w:footerReference` elements"""
21+
22+
type_ = RequiredAttribute('w:type', WD_HEADER_FOOTER)
23+
rId = RequiredAttribute('r:id', XsdString)
24+
25+
1826
class CT_PageMar(BaseOxmlElement):
1927
"""
2028
``<w:pgMar>`` element, defining page margins.
@@ -157,6 +165,13 @@ def left_margin(self, value):
157165
pgMar = self.get_or_add_pgMar()
158166
pgMar.left = value
159167

168+
def remove_headerReference(self, type_):
169+
"""Return rId of w:headerReference child of *type_* after removing it."""
170+
headerReference = self.get_headerReference(type_)
171+
rId = headerReference.rId
172+
self.remove(headerReference)
173+
return rId
174+
160175
@property
161176
def right_margin(self):
162177
"""

docx/parts/document.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ def document(self):
3939
"""
4040
return Document(self._element, self)
4141

42+
def drop_header_part(self, rId):
43+
"""Remove related header part identified by *rId*."""
44+
raise NotImplementedError
45+
4246
def get_or_add_image(self, image_descriptor):
4347
"""
4448
Return an (rId, image) 2-tuple for the image identified by

docx/section.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ def _add_header_part(self):
257257

258258
def _drop_header_part(self):
259259
"""Remove header definition associated with this section."""
260-
raise NotImplementedError
260+
rId = self._sectPr.remove_headerReference(WD_HEADER_FOOTER.PRIMARY)
261+
self._document_part.drop_header_part(rId)
261262

262263
@property
263264
def _has_header_part(self):

tests/test_section.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,15 @@ def it_can_change_whether_it_is_linked_to_previous_header(
386386
assert _drop_header_part_.call_args_list == [call(header)] * drop_calls
387387
assert _add_header_part_.call_args_list == [call(header)] * add_calls
388388

389+
def it_can_drop_the_related_header_part_to_help(self, document_part_):
390+
sectPr = element("w:sectPr{r:a=b}/w:headerReference{w:type=default,r:id=rId42}")
391+
header = _Header(sectPr, document_part_)
392+
393+
header._drop_header_part()
394+
395+
assert sectPr.xml == xml("w:sectPr{r:a=b}")
396+
document_part_.drop_header_part.assert_called_once_with("rId42")
397+
389398
def it_knows_when_it_has_a_header_part_to_help(self, has_header_part_fixture):
390399
sectPr, expected_value = has_header_part_fixture
391400
header = _Header(sectPr, None)
@@ -429,6 +438,10 @@ def is_linked_set_fixture(self, request):
429438
def _add_header_part_(self, request):
430439
return method_mock(request, _Header, "_add_header_part", autospec=True)
431440

441+
@pytest.fixture
442+
def document_part_(self, request):
443+
return instance_mock(request, DocumentPart)
444+
432445
@pytest.fixture
433446
def _drop_header_part_(self, request):
434447
return method_mock(request, _Header, "_drop_header_part", autospec=True)

tests/text/test_run.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@ def it_provides_access_to_its_font(self, font_fixture):
6969
Font_.assert_called_once_with(run._element)
7070
assert font is font_
7171

72-
def it_can_add_text(self, add_text_fixture):
73-
run, text_str, expected_xml, Text_ = add_text_fixture
72+
def it_can_add_text(self, add_text_fixture, Text_):
73+
r, text_str, expected_xml = add_text_fixture
74+
run = Run(r, None)
75+
7476
_text = run.add_text(text_str)
77+
7578
assert run._r.xml == expected_xml
7679
assert _text is Text_.return_value
7780

@@ -159,11 +162,11 @@ def add_tab_fixture(self, request):
159162
('w:r', 'fo ', 'w:r/w:t{xml:space=preserve}"fo "'),
160163
('w:r', 'f o', 'w:r/w:t"f o"'),
161164
])
162-
def add_text_fixture(self, request, Text_):
165+
def add_text_fixture(self, request):
163166
r_cxml, text, expected_cxml = request.param
164-
run = Run(element(r_cxml), None)
167+
r = element(r_cxml)
165168
expected_xml = xml(expected_cxml)
166-
return run, text, expected_xml, Text_
169+
return r, text, expected_xml
167170

168171
@pytest.fixture(params=[
169172
('w:r/w:rPr', 'bold', None),

tests/unitutil/cxml.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
# encoding: utf-8
22

3-
"""
4-
Parser for Compact XML Expression Language (CXEL) ('see-ex-ell'), a compact
5-
XML specification language I made up that's useful for producing XML element
6-
trees suitable for unit testing.
3+
"""Parser for Compact XML Expression Language (CXEL) ('see-ex-ell').
4+
5+
CXEL is a compact XML specification language I made up that's useful for producing XML
6+
element trees suitable for unit testing.
77
"""
88

9-
from __future__ import print_function
9+
from __future__ import absolute_import, division, print_function, unicode_literals
1010

1111
from pyparsing import (
12-
alphas, alphanums, Combine, dblQuotedString, delimitedList, Forward,
13-
Group, Literal, Optional, removeQuotes, stringEnd, Suppress, Word
12+
alphas,
13+
alphanums,
14+
Combine,
15+
dblQuotedString,
16+
delimitedList,
17+
Forward,
18+
Group,
19+
Literal,
20+
Optional,
21+
removeQuotes,
22+
stringEnd,
23+
Suppress,
24+
Word,
1425
)
1526

1627
from docx.oxml import parse_xml
@@ -105,16 +116,25 @@ def is_root(self, value):
105116
self._is_root = bool(value)
106117

107118
@property
108-
def nspfx(self):
119+
def local_nspfxs(self):
109120
"""
110-
The namespace prefix of this element, the empty string (``''``) if
111-
the tag is in the default namespace.
121+
The namespace prefixes local to this element, both on the tagname and
122+
all of its attributes. An empty string (``''``) is used to represent
123+
the default namespace for an element tag having no prefix.
112124
"""
113-
tagname = self._tagname
114-
idx = tagname.find(':')
115-
if idx == -1:
116-
return ''
117-
return tagname[:idx]
125+
def nspfx(name, is_element=False):
126+
idx = name.find(':')
127+
if idx == -1:
128+
return '' if is_element else None
129+
return name[:idx]
130+
131+
nspfxs = [nspfx(self._tagname, True)]
132+
for name, val in self._attrs:
133+
pfx = nspfx(name)
134+
if pfx is None or pfx in nspfxs or pfx == "xml":
135+
continue
136+
nspfxs.append(pfx)
137+
return nspfxs
118138

119139
@property
120140
def nspfxs(self):
@@ -129,7 +149,7 @@ def merge(seq, seq_2):
129149
continue
130150
seq.append(item)
131151

132-
nspfxs = [self.nspfx]
152+
nspfxs = self.local_nspfxs
133153
for child in self._children:
134154
merge(nspfxs, child.nspfxs)
135155
return nspfxs
@@ -223,12 +243,12 @@ def grammar():
223243

224244
# np:tagName ---------------------------------
225245
nspfx = Word(alphas)
226-
local_name = Word(alphas)
246+
local_name = Word(alphanums)
227247
tagname = Combine(nspfx + colon + local_name)
228248

229249
# np:attr_name=attr_val ----------------------
230250
attr_name = Word(alphas + ':')
231-
attr_val = Word(alphanums + ' -.%')
251+
attr_val = Word(alphanums + ' %-./:_')
232252
attr_def = Group(attr_name + equal + attr_val)
233253
attr_list = open_brace + delimitedList(attr_def) + close_brace
234254

0 commit comments

Comments
 (0)