1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
"""Custom element classes related to the numbering part."""
from docx.oxml.parser import OxmlElement
from docx.oxml.shared import CT_DecimalNumber
from docx.oxml.simpletypes import ST_DecimalNumber
from docx.oxml.xmlchemy import (
BaseOxmlElement,
OneAndOnlyOne,
RequiredAttribute,
ZeroOrMore,
ZeroOrOne,
)
class CT_Num(BaseOxmlElement):
"""``<w:num>`` element, which represents a concrete list definition instance, having
a required child <w:abstractNumId> that references an abstract numbering definition
that defines most of the formatting details."""
abstractNumId = OneAndOnlyOne("w:abstractNumId")
lvlOverride = ZeroOrMore("w:lvlOverride")
numId = RequiredAttribute("w:numId", ST_DecimalNumber)
def add_lvlOverride(self, ilvl):
"""Return a newly added CT_NumLvl (<w:lvlOverride>) element having its ``ilvl``
attribute set to `ilvl`."""
return self._add_lvlOverride(ilvl=ilvl)
@classmethod
def new(cls, num_id, abstractNum_id):
"""Return a new ``<w:num>`` element having numId of `num_id` and having a
``<w:abstractNumId>`` child with val attribute set to `abstractNum_id`."""
num = OxmlElement("w:num")
num.numId = num_id
abstractNumId = CT_DecimalNumber.new("w:abstractNumId", abstractNum_id)
num.append(abstractNumId)
return num
class CT_NumLvl(BaseOxmlElement):
"""``<w:lvlOverride>`` element, which identifies a level in a list definition to
override with settings it contains."""
startOverride = ZeroOrOne("w:startOverride", successors=("w:lvl",))
ilvl = RequiredAttribute("w:ilvl", ST_DecimalNumber)
def add_startOverride(self, val):
"""Return a newly added CT_DecimalNumber element having tagname
``w:startOverride`` and ``val`` attribute set to `val`."""
return self._add_startOverride(val=val)
class CT_NumPr(BaseOxmlElement):
"""A ``<w:numPr>`` element, a container for numbering properties applied to a
paragraph."""
ilvl = ZeroOrOne("w:ilvl", successors=("w:numId", "w:numberingChange", "w:ins"))
numId = ZeroOrOne("w:numId", successors=("w:numberingChange", "w:ins"))
# @ilvl.setter
# def _set_ilvl(self, val):
# """
# Get or add a <w:ilvl> child and set its ``w:val`` attribute to `val`.
# """
# ilvl = self.get_or_add_ilvl()
# ilvl.val = val
# @numId.setter
# def numId(self, val):
# """
# Get or add a <w:numId> child and set its ``w:val`` attribute to
# `val`.
# """
# numId = self.get_or_add_numId()
# numId.val = val
class CT_Numbering(BaseOxmlElement):
"""``<w:numbering>`` element, the root element of a numbering part, i.e.
numbering.xml."""
num = ZeroOrMore("w:num", successors=("w:numIdMacAtCleanup",))
def add_num(self, abstractNum_id):
"""Return a newly added CT_Num (<w:num>) element referencing the abstract
numbering definition identified by `abstractNum_id`."""
next_num_id = self._next_numId
num = CT_Num.new(next_num_id, abstractNum_id)
return self._insert_num(num)
def num_having_numId(self, numId):
"""Return the ``<w:num>`` child element having ``numId`` attribute matching
`numId`."""
xpath = './w:num[@w:numId="%d"]' % numId
try:
return self.xpath(xpath)[0]
except IndexError:
raise KeyError("no <w:num> element with numId %d" % numId)
@property
def _next_numId(self):
"""The first ``numId`` unused by a ``<w:num>`` element, starting at 1 and
filling any gaps in numbering between existing ``<w:num>`` elements."""
numId_strs = self.xpath("./w:num/@w:numId")
num_ids = [int(numId_str) for numId_str in numId_strs]
for num in range(1, len(num_ids) + 2):
if num not in num_ids:
break
return num
|