Passed
Push — main ( c5e6ce...6cb86e )
by Christophe
02:38 queued 01:04
created

pandoc_latex_margin._main   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 26
eloc 87
dl 0
loc 220
rs 10
c 0
b 0
f 0

6 Functions

Rating   Name   Duplication   Size   Complexity  
A add_definition() 0 28 3
A get_correct_margin() 0 27 2
C margin() 0 53 11
B prepare() 0 25 6
A main() 0 15 1
A finalize() 0 30 3
1
#!/usr/bin/env python
2
3
"""
4
Pandoc filter for changing margins in LaTeX.
5
"""
6
from __future__ import annotations
7
8
import re
9
from typing import Any
10
11
from panflute import (
12
    Doc,
13
    Element,
14
    MetaInlines,
15
    MetaList,
16
    RawBlock,
17
    RawInline,
18
    debug,
19
    run_filter,
20
)
21
22
23
# pylint:disable=line-too-long
24
def get_correct_margin(length: str) -> str:
25
    """
26
    Get the margin.
27
28
    Arguments
29
    ---------
30
    length
31
        A margin in LaTeX notation.
32
33
    Returns
34
    -------
35
    str
36
        A correct margin.
37
    """
38
    if (
39
        re.match(
40
            "^(-?([1-9][0-9]*)|0)(pt|mm|cm|in|ex|em|bp|pc|dd|cc|nd|nc|sp)$", length
41
        )
42
        is not None
43
    ):  # noqa: E501
44
        return length
45
    debug(
46
        "[WARNING] pandoc-latex-margin: "
47
        + length
48
        + " is not a correct LaTeX margin; using 0pt"
49
    )  # noqa: E501
50
    return "0pt"
51
52
53
# pylint: disable=inconsistent-return-statements
54
def margin(elem: Element, doc: Doc) -> list[Element] | None:
55
    """
56
    Add margin to element if needed.
57
58
    Arguments
59
    ---------
60
    elem
61
        A pandoc element
62
    doc
63
        The pandoc document
64
65
    Returns
66
    -------
67
    list[Element] | None
68
        A list of pandoc elements or None.
69
    """
70
    # Is it in the right format and is it a Div or CodeBlock?
71
    if doc.format in ("latex", "beamer") and elem.tag in ("Div", "CodeBlock"):
72
        left = None
73
        right = None
74
75
        # Get the classes
76
        classes = set(elem.classes)
77
78
        # Loop on all fontsize definition
79
        for definition in doc.defined:
80
            # Are the classes correct?
81
            if classes >= definition["classes"]:
82
                left = definition["left"]
83
                right = definition["right"]
84
                break
85
86
        # Is there a latex-margin-left attribute?
87
        if "latex-left-margin" in elem.attributes:
88
            left = get_correct_margin(elem.attributes["latex-left-margin"])
89
90
        # Is there a latex-margin-right attribute?
91
        if "latex-right-margin" in elem.attributes:
92
            right = get_correct_margin(elem.attributes["latex-right-margin"])
93
94
        if left is not None or right is not None:
95
            if left is None:
96
                left = "0pt"
97
            if right is None:
98
                right = "0pt"
99
            return [
100
                RawBlock(
101
                    "\\begin{pandocchangemargin}{" + left + "}{" + right + "}", "tex"
102
                ),  # noqa: E501
103
                elem,
104
                RawBlock("\\end{pandocchangemargin}", "tex"),
105
            ]
106
    return None
107
108
109
def prepare(doc: Doc) -> None:
110
    """
111
    Prepare the document.
112
113
    Arguments
114
    ---------
115
    doc
116
        The pandoc document
117
    """
118
    # Prepare the definitions
119
    doc.defined = []
120
121
    # Get the meta data
122
    meta = doc.get_metadata("pandoc-latex-margin")
123
124
    if isinstance(meta, list):
125
        # Loop on all definitions
126
        for definition in meta:
127
            # Verify the definition
128
            if (
129
                isinstance(definition, dict)
130
                and "classes" in definition
131
                and isinstance(definition["classes"], list)
132
            ):  # noqa: E501
133
                add_definition(doc.defined, definition)
134
135
136
def add_definition(defined: list[dict[str, Any]], definition: dict[str, Any]):
137
    """
138
    Add a definition.
139
140
    Arguments
141
    ---------
142
    defined
143
        A list of definition
144
    definition
145
        A new definition
146
    """
147
    # Get the classes
148
    classes = definition["classes"]
149
150
    # Get the left margin
151
    if "left" in definition:
152
        left = get_correct_margin(definition["left"])
153
    else:
154
        left = "0pt"
155
156
    # Get the left margin
157
    if "right" in definition:
158
        right = get_correct_margin(definition["right"])
159
    else:
160
        right = "0pt"
161
162
    # Add a definition
163
    defined.append({"classes": set(classes), "left": left, "right": right})
164
165
166
def finalize(doc: Doc) -> None:
167
    """
168
    Finalize the document.
169
170
    Arguments
171
    ---------
172
    doc
173
        The pandoc document
174
    """
175
    # Add header-includes if necessary
176
    if "header-includes" not in doc.metadata:
177
        doc.metadata["header-includes"] = MetaList()
178
    # Convert header-includes to MetaList if necessary
179
    elif not isinstance(doc.metadata["header-includes"], MetaList):
180
        doc.metadata["header-includes"] = MetaList(
181
            doc.metadata["header-includes"]
182
        )  # noqa: E501
183
184
    # Add useful LaTex definition
185
    doc.metadata["header-includes"].append(
186
        MetaInlines(
187
            RawInline(
188
                "\n".join(
189
                    [
190
                        "\\def\\pandocchangemargin#1#2{\\list{}{\\rightmargin#2\\leftmargin#1}\\item[]}",  # noqa: E501
191
                        "\\let\\endpandocchangemargin=\\endlist",
192
                        "",
193
                    ]
194
                ),
195
                "tex",
196
            )
197
        )
198
    )
199
200
201
def main(doc: Doc | None = None) -> Doc:
202
    """
203
    Process the transformation.
204
205
    Arguments
206
    ---------
207
    doc
208
        The pandoc document.
209
210
    Returns
211
    -------
212
    Doc
213
        The modified document.
214
    """
215
    return run_filter(margin, prepare=prepare, doc=doc, finalize=finalize)
216
217
218
if __name__ == "__main__":
219
    main()
220