Skip to content

Commit c38bf8e

Browse files
author
Steve Canny
committed
parfmt: add ParagraphFormat.line_spacing getter
1 parent 6629346 commit c38bf8e

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

docx/oxml/text/paragraph.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
Custom element classes related to paragraphs (CT_P).
55
"""
66

7-
from ...enum.text import WD_ALIGN_PARAGRAPH
7+
from ...enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
88
from ..ns import qn
9-
from ..simpletypes import ST_TwipsMeasure
9+
from ..simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure
1010
from ..xmlchemy import (
1111
BaseOxmlElement, OptionalAttribute, OxmlElement, RequiredAttribute,
1212
ZeroOrMore, ZeroOrOne
@@ -161,6 +161,33 @@ def spacing_before(self, value):
161161
return
162162
self.get_or_add_spacing().before = value
163163

164+
@property
165+
def spacing_line(self):
166+
"""
167+
The value of `w:spacing/@w:line` or |None| if not present.
168+
"""
169+
spacing = self.spacing
170+
if spacing is None:
171+
return None
172+
return spacing.line
173+
174+
@property
175+
def spacing_lineRule(self):
176+
"""
177+
The value of `w:spacing/@w:lineRule` as a member of the
178+
:ref:`WdLineSpacing` enumeration. Only the `MULTIPLE`, `EXACTLY`, and
179+
`AT_LEAST` members are used. It is the responsibility of the client
180+
to calculate the use of `SINGLE`, `DOUBLE`, and `MULTIPLE` based on
181+
the value of `w:spacing/@w:line` if that behavior is desired.
182+
"""
183+
spacing = self.spacing
184+
if spacing is None:
185+
return None
186+
lineRule = spacing.lineRule
187+
if lineRule is None and spacing.line is not None:
188+
return WD_LINE_SPACING.MULTIPLE
189+
return lineRule
190+
164191
@property
165192
def style(self):
166193
"""
@@ -193,3 +220,5 @@ class CT_Spacing(BaseOxmlElement):
193220
"""
194221
after = OptionalAttribute('w:after', ST_TwipsMeasure)
195222
before = OptionalAttribute('w:before', ST_TwipsMeasure)
223+
line = OptionalAttribute('w:line', ST_SignedTwipsMeasure)
224+
lineRule = OptionalAttribute('w:lineRule', WD_LINE_SPACING)

docx/text/paragraph.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
Paragraph-related proxy types.
55
"""
66

7-
from __future__ import absolute_import, print_function, unicode_literals
7+
from __future__ import (
8+
absolute_import, division, print_function, unicode_literals
9+
)
810

911
from ..enum.style import WD_STYLE_TYPE
12+
from ..enum.text import WD_LINE_SPACING
1013
from .run import Run
11-
from ..shared import ElementProxy, Parented
14+
from ..shared import ElementProxy, Parented, Pt
1215

1316

1417
class Paragraph(Parented):
@@ -155,6 +158,23 @@ def alignment(self, value):
155158
pPr = self._element.get_or_add_pPr()
156159
pPr.jc_val = value
157160

161+
@property
162+
def line_spacing(self):
163+
"""
164+
|float| or |Length| value specifying the space between baselines in
165+
successive lines of the paragraph. A value of |None| indicates line
166+
spacing is inherited from the style hierarchy. A float value, e.g.
167+
``2.0`` or ``1.75``, indicates spacing is applied in multiples of
168+
line heights. A |Length| value such as ``Pt(12)`` indicates spacing
169+
is a fixed height. The |Pt| value class is a convenient way to apply
170+
line spacing in units of points. Assigning |None| resets line spacing
171+
to inherit from the style hierarchy.
172+
"""
173+
pPr = self._element.pPr
174+
if pPr is None:
175+
return None
176+
return self._line_spacing(pPr.spacing_line, pPr.spacing_lineRule)
177+
158178
@property
159179
def space_after(self):
160180
"""
@@ -192,3 +212,18 @@ def space_before(self):
192212
@space_before.setter
193213
def space_before(self, value):
194214
self._element.get_or_add_pPr().spacing_before = value
215+
216+
@staticmethod
217+
def _line_spacing(spacing_line, spacing_lineRule):
218+
"""
219+
Return the line spacing value calculated from the combination of
220+
*spacing_line* and *spacing_lineRule*. Returns a |float| number of
221+
lines when *spacing_lineRule* is ``WD_LINE_SPACING.MULTIPLE``,
222+
otherwise a |Length| object of absolute line height is returned.
223+
Returns |None| when *spacing_line* is |None|.
224+
"""
225+
if spacing_line is None:
226+
return None
227+
if spacing_lineRule == WD_LINE_SPACING.MULTIPLE:
228+
return spacing_line / Pt(12)
229+
return spacing_line

tests/text/test_paragraph.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ def it_can_change_its_space_after(self, space_after_set_fixture):
315315
paragraph_format.space_after = value
316316
assert paragraph_format._element.xml == expected_xml
317317

318+
def it_knows_its_line_spacing(self, line_spacing_get_fixture):
319+
paragraph_format, expected_value = line_spacing_get_fixture
320+
assert paragraph_format.line_spacing == expected_value
321+
318322
# fixtures -------------------------------------------------------
319323

320324
@pytest.fixture(params=[
@@ -345,6 +349,19 @@ def alignment_set_fixture(self, request):
345349
expected_xml = xml(expected_cxml)
346350
return paragraph_format, value, expected_xml
347351

352+
@pytest.fixture(params=[
353+
('w:p', None),
354+
('w:p/w:pPr', None),
355+
('w:p/w:pPr/w:spacing', None),
356+
('w:p/w:pPr/w:spacing{w:line=420}', 1.75),
357+
('w:p/w:pPr/w:spacing{w:line=840,w:lineRule=exact}', Pt(42)),
358+
('w:p/w:pPr/w:spacing{w:line=840,w:lineRule=atLeast}', Pt(42)),
359+
])
360+
def line_spacing_get_fixture(self, request):
361+
p_cxml, expected_value = request.param
362+
paragraph_format = ParagraphFormat(element(p_cxml))
363+
return paragraph_format, expected_value
364+
348365
@pytest.fixture(params=[
349366
('w:p', None),
350367
('w:p/w:pPr', None),

0 commit comments

Comments
 (0)