chrisjsewell / ipypublish
1
""" a panflute filter to prepare document labelling in markdown files:
2

3
1) Add a ``$$reference`` key to the Document metadata
4

5
Then, for each Image, Math and Table found;
6

7
2) Extract labels and attributes to the right of Math or Table captions,
8
in the form; ``{#id .class-name a="an attribute"}``
9

10
3) If attributes found, remove them from the document and wrap the associated
11
   Math/Table in a Span/Div with the attributes and an additional class:
12
   ``labelled-Math`` or ``labelled-Table``
13

14
4) For all labelled Tables, Math and Images,
15
   place in metadata as e.g.
16
   meta["$$references"][label] = {"type": "Math", "number": 1}
17

18
For example:
19

20
    '$$a=1$$ {#a b=$2$}'
21

22

23
would be converted to this html:
24

25
.. code-block:: html
26

27
   <p>
28
   <span id="a" class="labelled-Math" data-b="2">
29
   <span class="math inline"><em>a</em> = 1</span>
30
   </span>
31
   </p>
32

33
"""
34 3
from panflute import Element, Doc, Table, Inline  # noqa: F401
35 3
import panflute as pf
36

37 3
from ipypublish.filters_pandoc.utils import (
38
    compare_version,
39
    get_panflute_containers,
40
    find_attributes,
41
)
42

43 3
LABELLED_IMAGE_CLASS = "labelled-Image"
44 3
LABELLED_MATH_CLASS = "labelled-Math"
45 3
LABELLED_TABLE_CLASS = "labelled-Table"
46

47 3
REFTYPE_TABLE = "Table"
48 3
REFTYPE_IMAGE = "Image"
49 3
REFTYPE_MATH = "Math"
50

51

52 3
def resolve_tables(element, doc):
53
    # type: (Table, Doc) -> None
54 3
    if not isinstance(element, (pf.Table)):
55 3
        return None
56

57 3
    ref_type = REFTYPE_TABLE
58

59 3
    attributes = None
60 3
    if element.caption:  # type: Inline
61
        # attributes = _find_attribute(element.caption[0],
62
        #                              allow_any=True, delete_preceding=False)
63 3
        attributes = find_attributes(
64
            element.caption[-1], search_left=True, include_element=True
65
        )
66

67 3
    if not attributes:
68 0
        return None
69

70
    # update count
71 3
    doc.refcount[ref_type] += 1
72
    # add to metadata
73 3
    doc.metadata["$$references"][attributes["id"]] = pf.MetaMap(
74
        **{"type": pf.MetaString(ref_type), "number": doc.refcount[ref_type]}
75
    )
76
    # remove attribute from caption
77 3
    element.caption = [el for el in element.caption if el not in attributes["elements"]]
78

79
    # wrap in a div
80 3
    return pf.Div(
81
        element,
82
        classes=["labelled-{}".format(ref_type)] + attributes["classes"],
83
        attributes=attributes["attributes"],
84
        identifier=attributes["id"],
85
    )
86

87

88 3
def resolve_equations_images(element, doc):
89
    # type: (Element, Doc) -> None
90

91
    # attribute equations in table captions / definition items?
92 3
    if not isinstance(element, get_panflute_containers(pf.Math)):
93 3
        return None
94

95 3
    if not element.content:
96 3
        return None
97

98 3
    to_delete = set()
99 3
    to_wrap = dict()
100

101 3
    subel = element.content[0]
102

103 3
    while subel:  # type: Element
104

105 3
        ref_type = None
106 3
        if isinstance(subel, pf.Math):
107 3
            ref_type = REFTYPE_MATH
108
        # elif isinstance(subel, pf.Table):
109
        #     ref_type = "Table"
110 3
        elif isinstance(subel, pf.Image):
111 3
            ref_type = REFTYPE_IMAGE
112
        else:
113 3
            subel = subel.next
114 3
            continue
115

116 3
        if isinstance(subel, pf.Image) and compare_version("1.16", ">="):
117
            # pandoc >= 1.16 already supports this
118
            # TODO for pandoc < 1.16 also look for attributes attached,
119
            # to the image path, as occurs with image references
120
            # see https://github.com/tomduck/pandoc-fignos/issues/14
121 3
            attributes = {
122
                "id": subel.identifier,
123
                # "classes": subel.classes,
124
                # "attributes": subel.attributes,
125
                "elements": [],
126
            }
127

128
        else:
129 3
            attributes = find_attributes(subel)
130 3
            if attributes:
131 3
                to_wrap[subel] = attributes
132 3
                for _ in attributes["elements"]:
133 3
                    subel = subel.next
134

135 3
        if attributes and attributes["id"]:
136
            # update count
137 3
            doc.refcount[ref_type] += 1
138
            # add to metadata
139 3
            doc.metadata["$$references"][attributes["id"]] = pf.MetaMap(
140
                **{"type": pf.MetaString(ref_type), "number": doc.refcount[ref_type]}
141
            )
142

143 3
            to_delete.update(attributes["elements"])
144

145 3
        subel = subel.next
146

147 3
    new_content = [
148
        pf.Span(
149
            el,
150
            classes=["labelled-{}".format(ref_type)] + to_wrap[el]["classes"],
151
            attributes=to_wrap[el]["attributes"],
152
            identifier=to_wrap[el]["id"],
153
        )
154
        if el in to_wrap
155
        else el
156
        for el in element.content
157
        if el not in to_delete
158
    ]
159

160
    # if isinstance(element, pf.Plain):
161
    #     return pf.Plain(*new_content)
162
    # else:
163
    #     return pf.Para(*new_content)
164 3
    element.content = new_content
165 3
    return element
166

167

168 3
def prepare(doc):
169
    # type: (Doc) -> None
170 3
    doc.refcount = {"Table": 0, "Image": 0, "Math": 0}
171 3
    doc.metadata["$$references"] = pf.MetaMap()
172

173

174 3
def finalize(doc):
175
    # type: (Doc) -> None
176 3
    del doc.refcount
177

178

179 3
def main(doc=None):
180
    # type: (Doc) -> None
181 3
    return pf.run_filters(
182
        [resolve_tables, resolve_equations_images], prepare, finalize, doc=doc
183
    )
184

185

186 3
if __name__ == "__main__":
187 0
    main()

Read our documentation on viewing source code .

Loading