Skip to content

Commit 9b371a4

Browse files
author
Steve Canny
committed
sect: add Section.page_width getter
1 parent 18f4b7d commit 9b371a4

File tree

5 files changed

+116
-23
lines changed

5 files changed

+116
-23
lines changed

docx/oxml/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
107107
register_element_cls('w:style', CT_Style)
108108
register_element_cls('w:styles', CT_Styles)
109109

110-
from docx.oxml.section import CT_SectPr, CT_SectType
110+
from docx.oxml.section import CT_PageSz, CT_SectPr, CT_SectType
111+
register_element_cls('w:pgSz', CT_PageSz)
111112
register_element_cls('w:sectPr', CT_SectPr)
112113
register_element_cls('w:type', CT_SectType)
113114

docx/oxml/section.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,45 @@
55
"""
66

77
from ..enum.section import WD_SECTION_START
8+
from .simpletypes import ST_TwipsMeasure
89
from .xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrOne
910

1011

12+
class CT_PageSz(BaseOxmlElement):
13+
"""
14+
``<w:pgSz>`` element, defining page dimensions and orientation.
15+
"""
16+
w = OptionalAttribute('w:w', ST_TwipsMeasure)
17+
18+
1119
class CT_SectPr(BaseOxmlElement):
1220
"""
1321
``<w:sectPr>`` element, the container element for section properties.
1422
"""
15-
type = ZeroOrOne('w:type')
23+
__child_sequence__ = (
24+
'w:footnotePr', 'w:endnotePr', 'w:type', 'w:pgSz', 'w:pgMar',
25+
'w:paperSrc', 'w:pgBorders', 'w:lnNumType', 'w:pgNumType', 'w:cols',
26+
'w:formProt', 'w:vAlign', 'w:noEndnote', 'w:titlePg',
27+
'w:textDirection', 'w:bidi', 'w:rtlGutter', 'w:docGrid',
28+
'w:printerSettings', 'w:sectPrChange',
29+
)
30+
type = ZeroOrOne('w:type', successors=(
31+
__child_sequence__[__child_sequence__.index('w:type')+1:]
32+
))
33+
pgSz = ZeroOrOne('w:pgSz', successors=(
34+
__child_sequence__[__child_sequence__.index('w:pgSz')+1:]
35+
))
36+
37+
@property
38+
def page_width(self):
39+
"""
40+
Value in EMU of the ``w`` attribute of the ``<w:pgSz>`` child
41+
element, or |None| if not present.
42+
"""
43+
pgSz = self.pgSz
44+
if pgSz is None:
45+
return None
46+
return pgSz.w
1647

1748
@property
1849
def start_type(self):

docx/section.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ def __init__(self, sectPr):
1515
super(Section, self).__init__()
1616
self._sectPr = sectPr
1717

18+
@property
19+
def page_width(self):
20+
"""
21+
Total page width used for this section, inclusive of all edge spacing
22+
values such as margins. Page orientation is taken into account, so
23+
for example, its expected value would be ``Inches(11)`` for
24+
letter-sized paper when orientation is landscape.
25+
"""
26+
return self._sectPr.page_width
27+
1828
@property
1929
def start_type(self):
2030
"""

tests/oxml/unitdata/section.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
from ...unitdata import BaseBuilder
88

99

10+
class CT_PageSzBuilder(BaseBuilder):
11+
__tag__ = 'w:pgSz'
12+
__nspfxs__ = ('w',)
13+
__attrs__ = ('w:w', 'w:h', 'w:orient', 'w:code')
14+
15+
1016
class CT_SectPrBuilder(BaseBuilder):
1117
__tag__ = 'w:sectPr'
1218
__nspfxs__ = ('w',)
@@ -19,6 +25,10 @@ class CT_SectTypeBuilder(BaseBuilder):
1925
__attrs__ = ('w:val',)
2026

2127

28+
def a_pgSz():
29+
return CT_PageSzBuilder()
30+
31+
2232
def a_sectPr():
2333
return CT_SectPrBuilder()
2434

tests/test_section.py

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
from docx.enum.section import WD_SECTION
1212
from docx.section import Section
13+
from docx.shared import Inches
1314

14-
from .oxml.unitdata.section import a_sectPr, a_type
15+
from .oxml.unitdata.section import a_pgSz, a_sectPr, a_type
1516

1617

1718
class DescribeSection(object):
@@ -25,41 +26,81 @@ def it_can_change_its_start_type(self, start_type_set_fixture):
2526
section.start_type = new_start_type
2627
assert section._sectPr.xml == expected_xml
2728

29+
def it_knows_its_page_width(self, page_width_get_fixture):
30+
section, expected_page_width = page_width_get_fixture
31+
assert section.page_width == expected_page_width
32+
2833
# fixtures -------------------------------------------------------
2934

3035
@pytest.fixture(params=[
31-
(None, WD_SECTION.NEW_PAGE),
32-
('continuous', WD_SECTION.CONTINUOUS),
33-
('nextPage', WD_SECTION.NEW_PAGE),
34-
('oddPage', WD_SECTION.ODD_PAGE),
35-
('evenPage', WD_SECTION.EVEN_PAGE),
36-
('nextColumn', WD_SECTION.NEW_COLUMN),
36+
(True, 1440, Inches(1)),
37+
(True, None, None),
38+
(False, None, None),
39+
])
40+
def page_width_get_fixture(self, request):
41+
has_pgSz_child, w, expected_page_width = request.param
42+
pgSz_bldr = self.pgSz_bldr(has_pgSz_child, w)
43+
sectPr = self.sectPr_bldr(pgSz_bldr).element
44+
section = Section(sectPr)
45+
return section, expected_page_width
46+
47+
@pytest.fixture(params=[
48+
(False, None, WD_SECTION.NEW_PAGE),
49+
(True, None, WD_SECTION.NEW_PAGE),
50+
(True, 'continuous', WD_SECTION.CONTINUOUS),
51+
(True, 'nextPage', WD_SECTION.NEW_PAGE),
52+
(True, 'oddPage', WD_SECTION.ODD_PAGE),
53+
(True, 'evenPage', WD_SECTION.EVEN_PAGE),
54+
(True, 'nextColumn', WD_SECTION.NEW_COLUMN),
3755
])
3856
def start_type_get_fixture(self, request):
39-
type_val, expected_start_type = request.param
40-
sectPr = self.sectPr_bldr(type_val).element
57+
has_type_child, type_val, expected_start_type = request.param
58+
type_bldr = self.type_bldr(has_type_child, type_val)
59+
sectPr = self.sectPr_bldr(type_bldr).element
4160
section = Section(sectPr)
4261
return section, expected_start_type
4362

4463
@pytest.fixture(params=[
45-
('oddPage', WD_SECTION.EVEN_PAGE, 'evenPage'),
46-
('nextPage', None, None),
47-
('continuous', WD_SECTION.NEW_PAGE, None),
48-
(None, WD_SECTION.NEW_COLUMN, 'nextColumn'),
64+
(True, 'oddPage', WD_SECTION.EVEN_PAGE, True, 'evenPage'),
65+
(True, 'nextPage', None, False, None),
66+
(False, None, WD_SECTION.NEW_PAGE, False, None),
67+
(True, 'continuous', WD_SECTION.NEW_PAGE, False, None),
68+
(True, None, WD_SECTION.NEW_PAGE, False, None),
69+
(True, None, WD_SECTION.NEW_COLUMN, True, 'nextColumn'),
4970
])
5071
def start_type_set_fixture(self, request):
51-
initial_type_val, new_type, expected_type_val = request.param
52-
sectPr = self.sectPr_bldr(initial_type_val).element
72+
(has_type_child, initial_type_val, new_type, has_type_child_after,
73+
expected_type_val) = request.param
74+
# section ----------------------
75+
type_bldr = self.type_bldr(has_type_child, initial_type_val)
76+
sectPr = self.sectPr_bldr(type_bldr).element
5377
section = Section(sectPr)
54-
expected_xml = self.sectPr_bldr(expected_type_val).xml()
78+
# expected_xml -----------------
79+
type_bldr = self.type_bldr(has_type_child_after, expected_type_val)
80+
expected_xml = self.sectPr_bldr(type_bldr).xml()
5581
return section, new_type, expected_xml
5682

5783
# fixture components ---------------------------------------------
5884

59-
def sectPr_bldr(self, start_type=None):
85+
def pgSz_bldr(self, has_pgSz, w):
86+
if not has_pgSz:
87+
return None
88+
pgSz_bldr = a_pgSz()
89+
if w is not None:
90+
pgSz_bldr.with_w(w)
91+
return pgSz_bldr
92+
93+
def sectPr_bldr(self, *child_bldrs):
6094
sectPr_bldr = a_sectPr().with_nsdecls()
61-
if start_type is not None:
62-
sectPr_bldr.with_child(
63-
a_type().with_val(start_type)
64-
)
95+
for child_bldr in child_bldrs:
96+
if child_bldr is not None:
97+
sectPr_bldr.with_child(child_bldr)
6598
return sectPr_bldr
99+
100+
def type_bldr(self, has_type_elm, val):
101+
if not has_type_elm:
102+
return None
103+
type_bldr = a_type()
104+
if val is not None:
105+
type_bldr.with_val(val)
106+
return type_bldr

0 commit comments

Comments
 (0)