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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
from .. import documents
from .. import results
from .xmlparser import node_types
def read_document_xml_element(element, numbering=None):
reader = _create_reader(numbering)
return reader(element)
def _create_reader(numbering):
_handlers = {}
_ignored_elements = set([
"w:bookmarkStart",
"w:bookmarkEnd",
"w:sectPr",
"w:proofErr",
"w:lastRenderedPageBreak",
"w:commentRangeStart",
"w:commentRangeEnd",
"w:commentReference",
"w:del"
])
def handler(name):
def add(func):
_handlers[name] = func
return add
@handler("w:t")
def text(element):
return results.success(documents.Text(_inner_text(element)))
@handler("w:r")
def run(element):
properties = element.find_child_or_null("w:rPr")
style_name = properties \
.find_child_or_null("w:rStyle") \
.attributes.get("w:val")
is_bold = properties.find_child("w:b")
is_italic = properties.find_child("w:i")
return _read_xml_elements(element.children) \
.map(lambda children: documents.run(
children=children,
style_name=style_name,
is_bold=is_bold,
is_italic=is_italic,
))
@handler("w:p")
def paragraph(element):
properties = element.find_child_or_null("w:pPr")
style_name = properties \
.find_child_or_null("w:pStyle") \
.attributes.get("w:val")
numbering_properties = properties.find_child("w:numPr")
if numbering_properties is None:
numbering = None
else:
numbering = _read_numbering_properties(numbering_properties)
return _read_xml_elements(element.children) \
.map(lambda children: documents.paragraph(
children=children,
style_name=style_name,
numbering=numbering,
))
def _read_numbering_properties(element):
num_id = element.find_child("w:numId").attributes["w:val"]
level_index = element.find_child("w:ilvl").attributes["w:val"]
return numbering.find_level(num_id, level_index)
@handler("w:body")
def body(element):
return _read_xml_elements(element.children)
@handler("w:document")
def document(element):
body_element = _find_child(element, "w:body")
return _read_xml_elements(body_element.children) \
.map(documents.document)
@handler("w:tab")
def tab(element):
return results.success(documents.tab())
def read(element):
handler = _handlers.get(element.name)
if handler is None:
if element.name not in _ignored_elements:
warning = results.warning("An unrecognised element was ignored: {0}".format(element.name))
return results.Result(None, [warning])
else:
return results.success(None)
else:
return handler(element)
def _read_xml_elements(elements):
return results.combine(map(read, elements)) \
.map(lambda values: filter(None, values))
return read
def _find(predicate, iterable):
for item in iterable:
if predicate(item):
return item
def _find_child(element, name):
return _find(lambda child: child.name == name, element.children)
def _inner_text(node):
if node.node_type == node_types.text:
return node.value
else:
return "".join(_inner_text(child) for child in node.children)
|