Completed
Push — master ( 349a7a...260a96 )
by Christophe
25s
created

finalize()   B

Complexity

Conditions 5

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
dl 0
loc 36
rs 8.0894
c 0
b 0
f 0
1
#!/usr/bin/env python
2
3
"""
4
Pandoc filter for adding admonition in LaTeX
5
"""
6
7
from panflute import *
8
import uuid
9
10
def default_environment():
11
    return {
12
        'env': 'env-' + str(uuid.uuid4()),
13
        'color': 'black',
14
        'position': 'left',
15
        'linewidth': 2,
16
        'margin': -4,
17
        'innermargin': 5,
18
    }
19
20
def x11colors():
21
    # See https://www.w3.org/TR/css-color-3/#svg-color
22
    return {
23
        'aliceblue': 'F0F8FF',
24
        'antiquewhite': 'FAEBD7',
25
        'aqua': '00FFFF',
26
        'aquamarine': '7FFFD4',
27
        'azure': 'F0FFFF',
28
        'beige': 'F5F5DC',
29
        'bisque': 'FFE4C4',
30
        'black': '000000',
31
        'blanchedalmond': 'FFEBCD',
32
        'blue': '0000FF',
33
        'blueviolet': '8A2BE2',
34
        'brown': 'A52A2A',
35
        'burlywood': 'DEB887',
36
        'cadetblue': '5F9EA0',
37
        'chartreuse': '7FFF00',
38
        'chocolate': 'D2691E',
39
        'coral': 'FF7F50',
40
        'cornflowerblue': '6495ED',
41
        'cornsilk': 'FFF8DC',
42
        'crimson': 'DC143C',
43
        'cyan': '00FFFF',
44
        'darkblue': '00008B',
45
        'darkcyan': '008B8B',
46
        'darkgoldenrod': 'B8860B',
47
        'darkgray': 'A9A9A9',
48
        'darkgreen': '006400',
49
        'darkgrey': 'A9A9A9',
50
        'darkkhaki': 'BDB76B',
51
        'darkmagenta': '8B008B',
52
        'darkolivegreen': '556B2F',
53
        'darkorange': 'FF8C00',
54
        'darkorchid': '9932CC',
55
        'darkred': '8B0000',
56
        'darksalmon': 'E9967A',
57
        'darkseagreen': '8FBC8F',
58
        'darkslateblue': '483D8B',
59
        'darkslategray': '2F4F4F',
60
        'darkslategrey': '2F4F4F',
61
        'darkturquoise': '00CED1',
62
        'darkviolet': '9400D3',
63
        'deeppink': 'FF1493',
64
        'deepskyblue': '00BFFF',
65
        'dimgray': '696969',
66
        'dimgrey': '696969',
67
        'dodgerblue': '1E90FF',
68
        'firebrick': 'B22222',
69
        'floralwhite': 'FFFAF0',
70
        'forestgreen': '228B22',
71
        'fuchsia': 'FF00FF',
72
        'gainsboro': 'DCDCDC',
73
        'ghostwhite': 'F8F8FF',
74
        'gold': 'FFD700',
75
        'goldenrod': 'DAA520',
76
        'gray': '808080',
77
        'green': '008000',
78
        'greenyellow': 'ADFF2F',
79
        'grey': '808080',
80
        'honeydew': 'F0FFF0',
81
        'hotpink': 'FF69B4',
82
        'indianred': 'CD5C5C',
83
        'indigo': '4B0082',
84
        'ivory': 'FFFFF0',
85
        'khaki': 'F0E68C',
86
        'lavender': 'E6E6FA',
87
        'lavenderblush': 'FFF0F5',
88
        'lawngreen': '7CFC00',
89
        'lemonchiffon': 'FFFACD',
90
        'lightblue': 'ADD8E6',
91
        'lightcoral': 'F08080',
92
        'lightcyan': 'E0FFFF',
93
        'lightgoldenrodyellow': 'FAFAD2',
94
        'lightgray': 'D3D3D3',
95
        'lightgreen': '90EE90',
96
        'lightgrey': 'D3D3D3',
97
        'lightpink': 'FFB6C1',
98
        'lightsalmon': 'FFA07A',
99
        'lightseagreen': '20B2AA',
100
        'lightskyblue': '87CEFA',
101
        'lightslategray': '778899',
102
        'lightslategrey': '778899',
103
        'lightsteelblue': 'B0C4DE',
104
        'lightyellow': 'FFFFE0',
105
        'lime': '00FF00',
106
        'limegreen': '32CD32',
107
        'linen': 'FAF0E6',
108
        'magenta': 'FF00FF',
109
        'maroon': '800000',
110
        'mediumaquamarine': '66CDAA',
111
        'mediumblue': '0000CD',
112
        'mediumorchid': 'BA55D3',
113
        'mediumpurple': '9370DB',
114
        'mediumseagreen': '3CB371',
115
        'mediumslateblue': '7B68EE',
116
        'mediumspringgreen': '00FA9A',
117
        'mediumturquoise': '48D1CC',
118
        'mediumvioletred': 'C71585',
119
        'midnightblue': '191970',
120
        'mintcream': 'F5FFFA',
121
        'mistyrose': 'FFE4E1',
122
        'moccasin': 'FFE4B5',
123
        'navajowhite': 'FFDEAD',
124
        'navy': '000080',
125
        'oldlace': 'FDF5E6',
126
        'olive': '808000',
127
        'olivedrab': '6B8E23',
128
        'orange': 'FFA500',
129
        'orangered': 'FF4500',
130
        'orchid': 'DA70D6',
131
        'palegoldenrod': 'EEE8AA',
132
        'palegreen': '98FB98',
133
        'paleturquoise': 'AFEEEE',
134
        'palevioletred': 'DB7093',
135
        'papayawhip': 'FFEFD5',
136
        'peachpuff': 'FFDAB9',
137
        'peru': 'CD853F',
138
        'pink': 'FFC0CB',
139
        'plum': 'DDA0DD',
140
        'powderblue': 'B0E0E6',
141
        'purple': '800080',
142
        'red': 'FF0000',
143
        'rosybrown': 'BC8F8F',
144
        'royalblue': '4169E1',
145
        'saddlebrown': '8B4513',
146
        'salmon': 'FA8072',
147
        'sandybrown': 'F4A460',
148
        'seagreen': '2E8B57',
149
        'seashell': 'FFF5EE',
150
        'sienna': 'A0522D',
151
        'silver': 'C0C0C0',
152
        'skyblue': '87CEEB',
153
        'slateblue': '6A5ACD',
154
        'slategray': '708090',
155
        'slategrey': '708090',
156
        'snow': 'FFFAFA',
157
        'springgreen': '00FF7F',
158
        'steelblue': '4682B4',
159
        'tan': 'D2B48C',
160
        'teal': '008080',
161
        'thistle': 'D8BFD8',
162
        'tomato': 'FF6347',
163
        'turquoise': '40E0D0',
164
        'violet': 'EE82EE',
165
        'wheat': 'F5DEB3',
166
        'white': 'FFFFFF',
167
        'whitesmoke': 'F5F5F5',
168
        'yellow': 'FFFF00',
169
        'yellowgreen': '9ACD32'
170
    }
171
172
def admonition(elem, doc):
173
    # Is it in the right format and is it Div or a CodeBlock?
174
    if doc.format == 'latex' and elem.tag in ['Div', 'CodeBlock']:
175
176
        # Is there a latex-admonition-color attribute?
177
        if 'latex-admonition-color' in elem.attributes:
178
            environment = define_environment(
179
                doc, 
180
                elem.attributes, 
181
                'latex-admonition-color', 
182
                'latex-admonition-position', 
183
                'latex-admonition-linewidth', 
184
                'latex-admonition-margin', 
185
                'latex-admonition-innermargin'
186
            )
187
            doc.added.append(environment)
188
            return add_latex(elem, environment)
189
        else:
190
            # Get the classes
191
            classes = set(elem.classes)
192
193
            # Loop on all fontsize definition
194
            for environment in doc.defined:
195
196
                # Are the classes correct?
197
                if classes >= environment['classes']:
198
                    return add_latex(elem,  environment)
199
200
def add_latex(elem,  environment):
201
    images = []
202
    def extract_images(elem, doc):
203
        # Extract image which is alone with a title
204
        if isinstance(elem, Para) and len(elem.content) == 1 and isinstance(elem.content[0], Image) and bool(elem.content[0].content):
205
            images.append(elem)
206
            return []
207
    # The images need to be placed after the framed environment
208
    return [
209
        RawBlock('\\begin{' + environment['env'] + '}', 'tex'),
210
        elem.walk(extract_images),
211
        RawBlock('\\end{' + environment['env'] + '}', 'tex')
212
    ] + images
213
214
def prepare(doc):
215
    doc.x11colors = x11colors()
216
217
   # Prepare the definitions
218
    doc.defined = []
219
    doc.added = []
220
221
    # Get the meta data
222
    meta = doc.get_metadata('pandoc-latex-admonition')
223
224
    if isinstance(meta, list):
225
226
        # Loop on all definitions
227
        for definition in meta:
228
229
            # Verify the definition
230
            if isinstance(definition, dict) and 'classes' in definition and isinstance(definition['classes'], list):
231
                environment = define_environment(doc, definition, 'color', 'position', 'linewidth', 'margin', 'innermargin')
232
                environment['classes'] = set(definition['classes'])
233
                doc.defined.append(environment)
234
235
def define_environment(doc, definition, key_color, key_position, key_linewidth, key_margin, key_innermargin):
236
    # Get the default environment
237
    environment = default_environment()
238
239
    # Get the color
240
    if key_color in definition:
241
        color = str(definition[key_color]).lower()
242
        if color in doc.x11colors:
243
            environment['color'] = color
244
        else:
245
            # color must be a valid x11 color (https://www.w3.org/TR/css-color-3/#svg-color)
246
            debug('[WARNING] pandoc-latex-admonition: ' + color + ' is not a valid x11 color; using ' + environment['color'])
247
248
    # Get the position
249
    if key_position in definition:
250
        environment['position'] = str(definition[key_position])
251
252
    # Get the line width
253
    if key_linewidth in definition:
254
        try:
255
            linewidth = int(str(definition[key_linewidth]))
256
            if linewidth <= 0:
257
                debug('[WARNING] pandoc-latex-admonition: linewidth must be a positivie integer; using ' + str(environment['linewidth']))
258
            else:
259
                environment['linewidth'] = linewidth
260
        except ValueError:
261
            debug('[WARNING] pandoc-latex-admonition: linewidth is not a valid; using ' + str(environment['linewidth']))
262
            
263
    # Get the margin
264
    if key_margin in definition:
265
        try:
266
            environment['margin'] = int(str(definition[key_margin]))
267
        except ValueError:
268
            debug('[WARNING] pandoc-latex-admonition: margin is not a valid; using ' + str(environment['margin']))
269
270
    # Get the inner margin
271
    if key_innermargin in definition:
272
        try:
273
            environment['innermargin'] = int(str(definition[key_innermargin]))
274
        except ValueError:
275
            debug('[WARNING] pandoc-latex-admonition: innermargin is not a valid; using ' + str(environment['innermargin']))
276
277
    return environment
278
279
def environment_option(inv, pos, linewidth, innermargin, margin, color):
280
    properties = [
281
        'topline=false',
282
        'bottomline=false',
283
        inv + 'line=false',
284
        'linewidth=' + str(linewidth) + 'pt',
285
        'inner' + pos + 'margin=' + str(innermargin) +'pt',
286
        pos + 'margin=' + str(margin) +'pt',
287
        'inner' + inv + 'margin=0pt',
288
        'linecolor=' + color,
289
        'skipabove=\\topskip'
290
    ]
291
    return '[' + ','.join(properties) + ']'
292
    
293
def finalize(doc):
294
    # Add header-includes if necessary
295
    if 'header-includes' not in doc.metadata:
296
        doc.metadata['header-includes'] = []
297
298
    # Add usefull LaTexPackage
299
    doc.metadata['header-includes'].append(MetaInlines(RawInline('\\usepackage{mdframed}', 'tex')))
300
    doc.metadata['header-includes'].append(MetaInlines(RawInline('\\usepackage{xcolor}', 'tex')))
301
    
302
    # Define x11 colors
303
    tex = []
304
    for name, color in doc.x11colors.items():
305
        tex.append('\\definecolor{' + name.lower() + '}{HTML}{' + color + '}')
306
    doc.metadata['header-includes'].append(MetaInlines(RawInline('\n'.join(tex), 'tex')))
307
308
    # Define specific environments
309
    for environment in doc.defined + doc.added:
310
        if environment['position'] == 'right':
311
           pos = 'right'
312
           inv = 'left'
313
        else:
314
           pos = 'left'
315
           inv = 'right'
316
        
317
        doc.metadata['header-includes'].append(MetaInlines(RawInline(
318
            '\\newmdenv' +
319
            environment_option(
320
                inv,
321
                pos,
322
                environment['linewidth'],
323
                environment['innermargin'],
324
                environment['margin'],
325
                environment['color']
326
            ) +
327
            '{' + environment['env'] + '}',
328
            'tex'
329
        )))
330
331
def main(doc = None):
332
    run_filter(admonition, prepare = prepare, finalize = finalize, doc = doc)
333
334
if __name__ == '__main__':
335
    main()
336
337