Skip to content

Commit 0d1dc09

Browse files
author
Steve Canny
committed
style: add styles oxml classes
1 parent b8a13ad commit 0d1dc09

5 files changed

Lines changed: 160 additions & 10 deletions

File tree

docx/oxml/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,19 @@
3030
register_custom_element_class('w:body', CT_Body)
3131
register_custom_element_class('w:document', CT_Document)
3232

33-
from docx.oxml.parts.numbering import CT_Num, CT_Numbering, CT_NumLvl
33+
from docx.oxml.parts.numbering import (
34+
CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr
35+
)
3436
register_custom_element_class('w:abstractNumId', CT_DecimalNumber)
37+
register_custom_element_class('w:ilvl', CT_DecimalNumber)
3538
register_custom_element_class('w:lvlOverride', CT_NumLvl)
3639
register_custom_element_class('w:num', CT_Num)
40+
register_custom_element_class('w:numId', CT_DecimalNumber)
41+
register_custom_element_class('w:numPr', CT_NumPr)
3742
register_custom_element_class('w:numbering', CT_Numbering)
3843

39-
from docx.oxml.parts.styles import CT_Styles
44+
from docx.oxml.parts.styles import CT_Style, CT_Styles
45+
register_custom_element_class('w:style', CT_Style)
4046
register_custom_element_class('w:styles', CT_Styles)
4147

4248
from docx.oxml.table import CT_Row, CT_Tbl, CT_TblGrid, CT_TblPr, CT_Tc

docx/oxml/parts/numbering.py

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,87 @@ def new(cls, ilvl):
7171
return OxmlElement('w:lvlOverride', {qn('w:ilvl'): str(ilvl)})
7272

7373

74+
class CT_NumPr(OxmlBaseElement):
75+
"""
76+
A ``<w:numPr>`` element, a container for numbering properties applied to
77+
a paragraph.
78+
"""
79+
def get_or_add_ilvl(self):
80+
"""
81+
Return the ilvl child element, newly added if not present.
82+
"""
83+
ilvl = self.ilvl
84+
if ilvl is None:
85+
ilvl = self._add_ilvl()
86+
return ilvl
87+
88+
def get_or_add_numId(self):
89+
"""
90+
Return the numId child element, newly added if not present.
91+
"""
92+
numId = self.numId
93+
if numId is None:
94+
numId = self._add_numId()
95+
return numId
96+
97+
@property
98+
def ilvl(self):
99+
return self.find(qn('w:ilvl'))
100+
101+
@ilvl.setter
102+
def ilvl(self, val):
103+
"""
104+
Get or add a <w:ilvl> child and set its ``w:val`` attribute to *val*.
105+
"""
106+
ilvl = self.get_or_add_ilvl()
107+
ilvl.val = val
108+
109+
@classmethod
110+
def new(cls):
111+
"""
112+
Return a new ``<w:numPr>`` element
113+
"""
114+
return OxmlElement('w:numPr')
115+
116+
@property
117+
def numId(self):
118+
return self.find(qn('w:numId'))
119+
120+
@numId.setter
121+
def numId(self, val):
122+
"""
123+
Get or add a <w:numId> child and set its ``w:val`` attribute to *val*.
124+
"""
125+
numId = self.get_or_add_numId()
126+
numId.val = val
127+
128+
def _add_ilvl(self, val=0):
129+
"""
130+
Return a newly added CT_DecimalNumber element having tagname 'w:ilvl'
131+
and ``val`` attribute set to *val*.
132+
"""
133+
ilvl = CT_DecimalNumber.new('w:ilvl', val)
134+
return self._insert_ilvl(ilvl)
135+
136+
def _add_numId(self, val=0):
137+
"""
138+
Return a newly added CT_DecimalNumber element having tagname
139+
'w:numId' and ``val`` attribute set to *val*.
140+
"""
141+
numId = CT_DecimalNumber.new('w:numId', val)
142+
return self._insert_numId(numId)
143+
144+
def _insert_ilvl(self, ilvl):
145+
return self.insert_element_before(
146+
ilvl, 'w:numId', 'w:numberingChange', 'w:ins'
147+
)
148+
149+
def _insert_numId(self, numId):
150+
return self.insert_element_before(
151+
numId, 'w:numberingChange', 'w:ins'
152+
)
153+
154+
74155
class CT_Numbering(OxmlBaseElement):
75156
"""
76157
``<w:numbering>`` element, the root element of a numbering part, i.e.
@@ -83,13 +164,7 @@ def add_num(self, abstractNum_id):
83164
"""
84165
next_num_id = self._next_numId
85166
num = CT_Num.new(next_num_id, abstractNum_id)
86-
# insert in proper sequence among children ---------
87-
successor = self.first_child_found_in('w:numIdMacAtCleanup')
88-
if successor is not None:
89-
successor.addprevious(num)
90-
else:
91-
self.append(num)
92-
return num
167+
return self._insert_num(num)
93168

94169
@property
95170
def num_lst(self):
@@ -109,6 +184,9 @@ def num_having_numId(self, numId):
109184
except IndexError:
110185
raise KeyError('no <w:num> element with numId %d' % numId)
111186

187+
def _insert_num(self, num):
188+
return self.insert_element_before(num, 'w:numIdMacAtCleanup')
189+
112190
@property
113191
def _next_numId(self):
114192
"""

docx/oxml/parts/styles.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,34 @@
44
Custom element classes related to the styles part
55
"""
66

7-
from docx.oxml.shared import OxmlBaseElement, qn
7+
from docx.oxml.shared import nsmap, OxmlBaseElement, qn
8+
9+
10+
class CT_Style(OxmlBaseElement):
11+
"""
12+
A ``<w:style>`` element, representing a style definition
13+
"""
14+
@property
15+
def pPr(self):
16+
return self.find(qn('w:pPr'))
817

918

1019
class CT_Styles(OxmlBaseElement):
1120
"""
1221
``<w:styles>`` element, the root element of a styles part, i.e.
1322
styles.xml
1423
"""
24+
def style_having_styleId(self, styleId):
25+
"""
26+
Return the ``<w:style>`` child element having ``styleId`` attribute
27+
matching *styleId*.
28+
"""
29+
xpath = './w:style[@w:styleId="%s"]' % styleId
30+
try:
31+
return self.xpath(xpath, namespaces=nsmap)[0]
32+
except IndexError:
33+
raise KeyError('no <w:style> element with styleId %d' % styleId)
34+
1535
@property
1636
def style_lst(self):
1737
"""

docx/oxml/shared.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ def first_child_found_in(self, *tagnames):
240240
return child
241241
return None
242242

243+
def insert_element_before(self, elm, *tagnames):
244+
successor = self.first_child_found_in(*tagnames)
245+
if successor is not None:
246+
successor.addprevious(elm)
247+
else:
248+
self.append(elm)
249+
return elm
250+
243251
@property
244252
def xml(self):
245253
"""
@@ -272,6 +280,11 @@ def val(self):
272280
number_str = self.get(qn('w:val'))
273281
return int(number_str)
274282

283+
@val.setter
284+
def val(self, val):
285+
decimal_number_str = '%d' % val
286+
self.set(qn('w:val'), decimal_number_str)
287+
275288

276289
class CT_OnOff(OxmlBaseElement):
277290
"""

docx/oxml/text.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
(CT_R).
66
"""
77

8+
from docx.oxml.parts.numbering import CT_NumPr
89
from docx.oxml.shared import (
910
CT_String, nsdecls, OxmlBaseElement, OxmlElement, oxml_fromstring, qn
1011
)
@@ -115,6 +116,15 @@ class CT_PPr(OxmlBaseElement):
115116
"""
116117
``<w:pPr>`` element, containing the properties for a paragraph.
117118
"""
119+
def get_or_add_numPr(self):
120+
"""
121+
Return the numPr child element, newly added if not present.
122+
"""
123+
numPr = self.numPr
124+
if numPr is None:
125+
numPr = self._add_numPr()
126+
return numPr
127+
118128
def get_or_add_pStyle(self):
119129
"""
120130
Return the pStyle child element, newly added if not present.
@@ -133,6 +143,13 @@ def new():
133143
pPr = oxml_fromstring(xml)
134144
return pPr
135145

146+
@property
147+
def numPr(self):
148+
"""
149+
``<w:numPr>`` child element or None if not present.
150+
"""
151+
return self.find(qn('w:numPr'))
152+
136153
@property
137154
def pStyle(self):
138155
"""
@@ -170,10 +187,26 @@ def style(self, style):
170187
else:
171188
self.pStyle.val = style
172189

190+
def _add_numPr(self):
191+
numPr = CT_NumPr.new()
192+
return self._insert_numPr(numPr)
193+
173194
def _add_pStyle(self, style):
174195
pStyle = CT_String.new_pStyle(style)
175196
return self._insert_pStyle(pStyle)
176197

198+
def _insert_numPr(self, numPr):
199+
return self.insert_element_before(
200+
numPr, 'w:suppressLineNumbers', 'w:pBdr', 'w:shd', 'w:tabs',
201+
'w:suppressAutoHyphens', 'w:kinsoku', 'w:wordWrap',
202+
'w:overflowPunct', 'w:topLinePunct', 'w:autoSpaceDE',
203+
'w:autoSpaceDN', 'w:bidi', 'w:adjustRightInd', 'w:snapToGrid',
204+
'w:spacing', 'w:ind', 'w:contextualSpacing', 'w:mirrorIndents',
205+
'w:suppressOverlap', 'w:jc', 'w:textDirection',
206+
'w:textAlignment', 'w:textboxTightWrap', 'w:outlineLvl',
207+
'w:divId', 'w:cnfStyle', 'w:rPr', 'w:sectPr', 'w:pPrChange'
208+
)
209+
177210
def _insert_pStyle(self, pStyle):
178211
self.insert(0, pStyle)
179212
return pStyle

0 commit comments

Comments
 (0)