aboutsummaryrefslogtreecommitdiff
path: root/mammoth/docx/document_xml.py
blob: 59f9920705bb9b5a6e717f46c75c0c2da7b95d9f (plain)
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
from .. import documents
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 = {}

    def handler(name):
        def add(func):
            _handlers[name] = func
            
        return add

    @handler("w:t")
    def text(element):
        return 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 documents.run(
            children=_read_xml_elements(element.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)
        
        children = _read_xml_elements(element.children)
        return documents.paragraph(children, style_name, 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 documents.document(_read_xml_elements(body_element.children))
    
    
    @handler("w:tab")
    def tab(element):
        return documents.tab()
    
    def read(element):
        handler = _handlers.get(element.name)
        if handler is None:
            return None
        else:
            return handler(element)
        

    def _read_xml_elements(elements):
        return filter(None, map(read, elements))
    
    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)