1
""" a panflute filter to format Span element
2
representations of RawInline elements
3

4
The :py:mod:`ipypublish.filters_pandoc.prepare_raw` filter should be run
5
first to access the functionality below:
6

7
"""
8 1
import itertools
9

10
# from textwrap import fill as textwrap
11

12 1
from panflute import Element, Doc, Span  # noqa: F401
13 1
import panflute as pf
14

15 1
from ipypublish.filters_pandoc.definitions import (
16
    CONVERTED_OTHER_CLASS,
17
    CONVERTED_DIRECTIVE_CLASS,
18
    IPUB_META_ROUTE,
19
)
20

21

22 1
def process_raw_spans(container, doc):
23
    # type: (Span, Doc) -> Element
24 1
    if not isinstance(container, (pf.Span, pf.Div)):
25 1
        return None
26

27 1
    hide_raw = doc.get_metadata(IPUB_META_ROUTE + ".hide_raw", False)
28

29 1
    if CONVERTED_OTHER_CLASS in container.classes and isinstance(container, pf.Span):
30 1
        if doc.format == "rst" and container.attributes["format"] == "latex":
31 1
            if container.attributes["tag"] in ["todo"]:
32 1
                return pf.Str(
33
                    "\n\n.. {}:: {}\n\n".format(
34
                        container.attributes["tag"], container.attributes["content"]
35
                    )
36
                )
37 0
            if container.attributes["tag"] == "ensuremath":
38 0
                return pf.RawInline(
39
                    ":math:`{}`".format(container.attributes["content"]), format="rst"
40
                )
41

42 1
        return pf.RawInline(
43
            container.attributes.get("original"), format=container.attributes["format"]
44
        )
45

46 1
    if CONVERTED_DIRECTIVE_CLASS in container.classes and isinstance(container, pf.Div):
47
        # convert the directive head, which will be e.g.
48
        # Para(Str(..) Space Str(toctree::) SoftBreak Str(:maxdepth:) Space Str(2) SoftBreak Str(:numbered:))  # noqa
49
        # we need to spilt on the soft breaks,
50
        # place them on a new line and re-indent them
51

52 1
        if doc.format in ("rst"):
53

54
            # split into lines by soft breaks
55 1
            header_lines = [
56
                list(y)
57
                for x, y in itertools.groupby(
58
                    container.content[0].content, lambda z: isinstance(z, pf.SoftBreak)
59
                )
60
                if not x
61
            ]
62

63
            # wrap each line in a Para and convert block with pandoc
64 1
            head_doc = pf.Doc(*[pf.Para(*l) for l in header_lines])
65 1
            head_doc.api_version = doc.api_version
66 1
            head_str = pf.convert_text(
67
                head_doc, input_format="panflute", output_format=doc.format
68
            )
69
            # remove blank lines and indent
70 1
            head_str = head_str.replace("\n\n", "\n    ") + "\n\n"
71 1
            head_block = pf.RawBlock(head_str, format=doc.format)
72

73 1
            if len(container.content) == 1:
74 0
                return head_block
75

76
            # split into lines by soft breaks, we use indicators to tell
77
            # us where to indent in the converted text
78 1
            body_blocks = []
79 1
            for block in container.content[1:]:
80 1
                new_elements = [pf.RawInline("%^*", format=doc.format)]
81 1
                for el in block.content:
82 1
                    if isinstance(el, pf.SoftBreak):
83 1
                        new_elements.append(pf.RawInline("?&@", format=doc.format))
84
                    else:
85 1
                        new_elements.append(el)
86 1
                block.content = new_elements
87 1
                body_blocks.append(block)
88

89
            # convert body content with pandoc
90 1
            body_doc = pf.Doc(*body_blocks)
91 1
            body_doc.api_version = doc.api_version
92 1
            body_str = pf.convert_text(
93
                body_doc, input_format="panflute", output_format=doc.format
94
            )
95
            # raise ValueError(body_blocks)
96 1
            body_str = body_str.replace("%^*", "    ").replace("?&@", "\n    ")
97

98
            # ensure all lines are indented correctly
99
            # (doesn't occur by default?)
100 1
            body_str = (
101
                "\n".join(
102
                    [
103
                        "    " + l.lstrip() if l.strip() else l
104
                        for l in body_str.splitlines()
105
                    ]
106
                )
107
                + "\n\n"
108
            )
109

110 1
            body_block = pf.RawBlock(body_str, format=doc.format)
111 1
            return [head_block, body_block]
112

113 1
        elif (
114
            doc.format in ("html", "html5") and container.attributes["format"] == "rst"
115
        ):
116

117 0
            if hide_raw:
118 0
                return []
119

120 0
            head_para = pf.Para(
121
                *[
122
                    pf.RawInline("<br>" + "&nbsp" * 4)
123
                    if isinstance(c, pf.SoftBreak)
124
                    else c
125
                    for c in container.content[0].content
126
                ]
127
            )
128 0
            head_str = pf.convert_text(
129
                head_para, input_format="panflute", output_format=doc.format
130
            )
131

132 0
            if len(container.content) > 1:
133

134 0
                body_doc = pf.Doc(*container.content[1:])
135 0
                body_doc.api_version = doc.api_version
136 0
                body_str = pf.convert_text(
137
                    body_doc, input_format="panflute", output_format=doc.format
138
                )
139 0
                body_str = (
140
                    '<p></p><div style="margin-left: 20px">' "{0}</div>"
141
                ).format(body_str)
142
            else:
143 0
                body_str = ""
144

145 0
            return pf.RawBlock(
146
                '<div {0} style="background-color:rgba(10, 225, 10, .2)">'
147
                "{1}{2}"
148
                "</div>".format(
149
                    container.attributes.get("directive", ""), head_str, body_str
150
                ),
151
                format="html",
152
            )
153

154 1
        elif doc.format in ("tex", "latex") and container.attributes["format"] == "rst":
155

156 1
            if hide_raw:
157 0
                return []
158

159 1
            directive = container.attributes.get("directive", "")
160 1
            inline = container.attributes.get("inline", "")
161
            # TODO handle directive with options and/or inline body
162
            # e.g. .. figure:: path/to/figure
163
            #          :centre:
164

165 1
            box_open = (
166
                "\\begin{{mdframed}}"
167
                "[frametitle={{{0}}},frametitlerule=true]".format(directive)
168
            )
169 1
            if inline:
170 1
                box_open += "\n\\mdfsubtitle{{{0}}}".format(inline)
171 1
            box_close = "\\end{mdframed}"
172

173 1
            if len(container.content) == 1:
174 0
                return pf.RawBlock(box_open + box_close, format="tex")
175
            else:
176 1
                return (
177
                    [pf.RawBlock(box_open, format="tex")]
178
                    + list(container.content[1:])
179
                    + [pf.RawBlock(box_close, format="tex")]
180
                )
181

182 0
        return pf.RawBlock(
183
            pf.stringify(pf.Doc(*container.content)),
184
            format=container.attributes["format"],
185
        )
186

187 1
    if CONVERTED_OTHER_CLASS in container.classes and isinstance(container, pf.Div):
188 1
        return pf.RawBlock(
189
            pf.stringify(pf.Doc(*container.content)),
190
            format=container.attributes["format"],
191
        )
192

193

194
# now unused
195
# def split_soft_breaks(container,
196
#                       indent=4, fmt="rst", indent_first=False,
197
#                       pre_content="", post_content="",
198
#                       pre_chunk="", post_chunk="",
199
#                       linebreak="\n", raw_indent=None):
200
#     """rst conversion doesn't recognise soft breaks as new lines,
201
#     so add them manually and return a list containing the new elements
202
#     """
203
#     content = []
204
#     if pre_content:
205
#         content.append(pf.RawBlock(pre_content, fmt))
206

207
#     chunks = [list(y) for x, y in itertools.groupby(
208
#         container.content,
209
#         lambda z: isinstance(z, pf.SoftBreak)) if not x]
210

211
#     for i, chunk in enumerate(chunks):
212
#         if i > 0 or indent_first:
213
#             if raw_indent is not None:
214
#                 chunk = [pf.RawInline(raw_indent, fmt)] * indent + chunk
215
#             else:
216
#                 chunk = [pf.Space()] * indent + chunk
217

218
#         if pre_chunk:
219
#             content.append(pf.RawBlock(pre_chunk, fmt))
220
#         content.append(pf.Plain(*chunk))
221
#         content.append(pf.RawBlock(linebreak, fmt))
222
#         if post_chunk:
223
#             content.append(pf.RawBlock(post_chunk, fmt))
224

225
#     # if isinstance(container, pf.Para):
226
#     #     content.append(pf.RawBlock(linebreak, fmt))
227

228
#     if post_content:
229
#         content.append(pf.RawBlock(post_content, fmt))
230

231
#     return content
232

233

234 1
def process_code_latex(code, doc):
235
    # type: (pf.CodeBlock, Doc) -> Element
236 1
    if doc.format not in ("tex", "latex"):
237 1
        return None
238 1
    if not isinstance(code, pf.CodeBlock):
239 1
        return None
240
    # TODO line wrapping
241 0
    return [
242
        pf.RawBlock("\\begin{mdframed}", format=doc.format),
243
        code,
244
        pf.RawBlock("\\end{mdframed}", format=doc.format),
245
    ]
246

247

248 1
def prepare(doc):
249
    # type: (Doc) -> None
250 1
    pass
251

252

253 1
def finalize(doc):
254
    # type: (Doc) -> None
255 1
    pass
256

257

258 1
def main(doc=None):
259
    # type: (Doc) -> None
260
    """
261
    """
262 1
    return pf.run_filters(
263
        [process_raw_spans, process_code_latex], prepare, finalize, doc=doc
264
    )
265

266

267 1
if __name__ == "__main__":
268 0
    main()

Read our documentation on viewing source code .

Loading