Skip to content

Commit 16afd9b

Browse files
Victor VolleSteve Canny
authored andcommitted
run: add Run.style setter
1 parent b0e3c80 commit 16afd9b

File tree

5 files changed

+77
-10
lines changed

5 files changed

+77
-10
lines changed

docx/oxml/shared.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,14 @@ def new_pStyle(cls, val):
333333
"""
334334
return OxmlElement('w:pStyle', attrs={qn('w:val'): val})
335335

336+
@classmethod
337+
def new_rStyle(cls, val):
338+
"""
339+
Return a new ``<w:rStyle>`` element with ``val`` attribute set to
340+
*val*.
341+
"""
342+
return OxmlElement('w:rStyle', attrs={qn('w:val'): val})
343+
336344
@property
337345
def val(self):
338346
return self.get(qn('w:val'))

docx/oxml/text.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,14 +270,23 @@ def rPr(self):
270270
@property
271271
def style(self):
272272
"""
273-
String contained in w:val attribute of <w:pStyle> grandchild, or
273+
String contained in w:val attribute of <w:rStyle> grandchild, or
274274
|None| if that element is not present.
275275
"""
276276
rPr = self.rPr
277277
if rPr is None:
278278
return None
279279
return rPr.style
280280

281+
@style.setter
282+
def style(self, style):
283+
"""
284+
Set the character style of this <w:r> element to *style*. If *style*
285+
is None, remove the style element.
286+
"""
287+
rPr = self.get_or_add_rPr()
288+
rPr.style = style
289+
281290
@property
282291
def t_lst(self):
283292
"""
@@ -609,6 +618,11 @@ def remove_outline(self):
609618
for outline in outline_lst:
610619
self.remove(outline)
611620

621+
def remove_rStyle(self):
622+
rStyle = self.rStyle
623+
if rStyle is not None:
624+
self.remove(rStyle)
625+
612626
def remove_rtl(self):
613627
rtl_lst = self.findall(qn('w:rtl'))
614628
for rtl in rtl_lst:
@@ -709,6 +723,20 @@ def style(self):
709723
return None
710724
return rStyle.val
711725

726+
@style.setter
727+
def style(self, style):
728+
"""
729+
Set val attribute of <w:rStyle> child element to *style*, adding a
730+
new element if necessary. If *style* is |None|, remove the <w:rStyle>
731+
element if present.
732+
"""
733+
if style is None:
734+
self.remove_rStyle()
735+
elif self.rStyle is None:
736+
self._add_rStyle(style)
737+
else:
738+
self.rStyle.val = style
739+
712740
@property
713741
def vanish(self):
714742
"""
@@ -723,6 +751,11 @@ def webHidden(self):
723751
"""
724752
return self.find(qn('w:webHidden'))
725753

754+
def _add_rStyle(self, style):
755+
rStyle = CT_String.new_rStyle(style)
756+
self.insert(0, rStyle)
757+
return rStyle
758+
726759

727760
class CT_Text(OxmlBaseElement):
728761
"""

docx/text.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,17 @@ def strike(self):
304304
def style(self):
305305
"""
306306
Read/write. The string name of the character style applied to this
307-
run, or |None| if it has no directly-applied character style.
307+
run, or |None| if it has no directly-applied character style. Setting
308+
this property to |None| causes any directly-applied character style
309+
to be removed such that the run inherits character formatting from
310+
its containing paragraph.
308311
"""
309312
return self._r.style
310313

314+
@style.setter
315+
def style(self, char_style):
316+
self._r.style = char_style
317+
311318
@property
312319
def text(self):
313320
"""

features/run-char-style.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ Feature: Each run has a read/write style
1515
| Strong |
1616

1717

18-
@wip
1918
Scenario Outline: Set the style of a run
2019
Given a run having style <char style>
2120
When I set the character style of the run to <new char style>

tests/test_text.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ def it_knows_its_character_style(self, style_get_fixture):
139139
run, expected_style = style_get_fixture
140140
assert run.style == expected_style
141141

142+
def it_can_change_its_character_style(self, style_set_fixture):
143+
run, style, expected_xml = style_set_fixture
144+
run.style = style
145+
assert run._r.xml == expected_xml
146+
142147
def it_can_add_text(self, add_text_fixture):
143148
run, text_str, expected_xml, Text_ = add_text_fixture
144149
_text = run.add_text(text_str)
@@ -313,16 +318,24 @@ def bool_prop_set_fixture(self, request):
313318
@pytest.fixture(params=['Foobar', None])
314319
def style_get_fixture(self, request):
315320
style = request.param
316-
r_bldr = an_r().with_nsdecls()
317-
if style is not None:
318-
r_bldr.with_child(
319-
an_rPr().with_child(
320-
an_rStyle().with_val(style))
321-
)
322-
r = r_bldr.element
321+
r = self.r_bldr_with_style(style).element
323322
run = Run(r)
324323
return run, style
325324

325+
@pytest.fixture(params=[
326+
(None, None),
327+
(None, 'Foobar'),
328+
('Foobar', None),
329+
('Foobar', 'Foobar'),
330+
('Foobar', 'Barfoo'),
331+
])
332+
def style_set_fixture(self, request):
333+
before_style, after_style = request.param
334+
r = self.r_bldr_with_style(before_style).element
335+
run = Run(r)
336+
expected_xml = self.r_bldr_with_style(after_style).xml()
337+
return run, after_style, expected_xml
338+
326339
@pytest.fixture
327340
def text_prop_fixture(self, Text_):
328341
r = (
@@ -340,6 +353,13 @@ def run(self):
340353
r = an_r().with_nsdecls().element
341354
return Run(r)
342355

356+
def r_bldr_with_style(self, style):
357+
rPr_bldr = an_rPr()
358+
if style is not None:
359+
rPr_bldr.with_child(an_rStyle().with_val(style))
360+
r_bldr = an_r().with_nsdecls().with_child(rPr_bldr)
361+
return r_bldr
362+
343363
@pytest.fixture
344364
def Text_(self, request):
345365
return class_mock(request, 'docx.text.Text')

0 commit comments

Comments
 (0)