Completed
Push — master ( 659e14...e3a80b )
by Christophe
01:15
created

remove_useless_latex()   A

Complexity

Conditions 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
#!/usr/bin/env python
2
3
"""
4
Pandoc filter to number all kinds of things.
5
"""
6
7
from panflute import *
8
from functools import partial
9
import re
10
import unicodedata
11
import copy
12
import itertools
13
14
class Numbered(object):
15
    __slots__ = [
16
        '_elem',
17
        '_doc',
18
        '_match',
19
        '_tag',
20
        '_entry',
21
        '_link',
22
        '_caption',
23
        '_title',
24
        '_description',
25
        '_category',
26
        '_basic_category',
27
        '_first_section_level',
28
        '_last_section_level',
29
        '_leading',
30
        '_number',
31
        '_global_number',
32
        '_section_number',
33
        '_local_number',
34
    ]
35
36
    @property
37
    def tag(self):
38
        return self._tag
39
40
    @property
41
    def entry(self):
42
        return self._entry
43
44
    @property
45
    def link(self):
46
        return self._link
47
48
    @property
49
    def title(self):
50
        return self._title
51
52
    @property
53
    def description(self):
54
        return self._description
55
56
    @property
57
    def global_number(self):
58
        return self._global_number
59
60
    @property
61
    def section_number(self):
62
        return self._section_number
63
64
    @property
65
    def local_number(self):
66
        return self._local_number
67
68
    @property
69
    def category(self):
70
        return self._category
71
72
    @property
73
    def caption(self):
74
        return self._caption
75
76
    number_regex = '#((?P<prefix>[a-zA-Z][\w.-]*):)?(?P<name>[a-zA-Z][\w:.-]*)?'
77
    _regex = '(?P<header>(?P<hidden>(-\.)*)(\+\.)*)'
78
    header_regex = '^' + _regex + '$'
79
    marker_regex = '^' + _regex + number_regex + '$'
80
    double_sharp_regex = '^' + _regex + '#' + number_regex + '$'
81
82
    @staticmethod
83
    def _remove_accents(string):
84
        nfkd_form = unicodedata.normalize('NFKD', string)
85
        return u''.join([c for c in nfkd_form if not unicodedata.combining(c)])
86
87
    @staticmethod
88
    def _identifier(string):
89
        # replace invalid characters by dash
90
        string = re.sub('[^0-9a-zA-Z_-]+', '-', Numbered._remove_accents(string.lower()))
91
92
        # Remove leading digits
93
        string = re.sub('^[^a-zA-Z]+', '', string)
94
95
        return string
96
97
    def __init__(self, elem, doc):
98
        self._elem = elem
99
        self._doc = doc
100
        self._entry = Span(classes=['pandoc-numbering-entry'])
101
        self._link = Span(classes=['pandoc-numbering-link'])
102
        self._tag = None
103
        if len(self._get_content()) > 0 and isinstance(self._get_content()[-1], Str):
104
            self._match = re.match(Numbered.marker_regex, self._get_content()[-1].text)
105
            if self._match:
106
                self._replace_marker()
107
            elif re.match(Numbered.double_sharp_regex, self._get_content()[-1].text):
108
                self._replace_double_sharp()
109
110
    def _set_content(self, content):
111
        if isinstance(self._elem, Para):
112
            self._elem.content = content
113
        elif isinstance(self._elem, DefinitionItem):
114
            self._elem.term = content
115
116
    def _get_content(self):
117
        if isinstance(self._elem, Para):
118
            return self._elem.content
119
        elif isinstance(self._elem, DefinitionItem):
120
            return self._elem.term
121
122
    def _replace_double_sharp(self):
123
        self._get_content()[-1].text = self._get_content()[-1].text.replace('##', '#', 1)
124
125
    def _replace_marker(self):
126
        self._compute_title()
127
        self._compute_description()
128
        self._compute_basic_category()
129
        self._compute_levels()
130
        self._compute_section_number()
131
        self._compute_leading()
132
        self._compute_category()
133
        self._compute_number()
134
        self._compute_tag()
135
        self._compute_local_number()
136
        self._compute_global_number()
137
        self._compute_data()
138
139
    def _compute_title(self):
140
        self._title = []
141
        if isinstance(self._get_content()[-3], Str) and self._get_content()[-3].text[-1:] == ')':
142
            for (i, item) in enumerate(self._get_content()):
143
                if isinstance(item, Str) and item.text[0] == '(':
144
                    self._title = self._get_content()[i:-2]
145
                    # Detach from original parent
146
                    self._title.parent = None
147
                    self._title[0].text = self._title[0].text[1:]
148
                    self._title[-1].text = self._title[-1].text[:-1]
149
                    del self._get_content()[i-1:-2]
150
                    break
151
        self._title = list(self._title)
152
153
    def _compute_description(self):
154
        self._description = self._get_content()[:-2]
155
        # Detach from original parent
156
        self._description.parent = None
157
        self._description = list(self._description)
158
159
    def _compute_basic_category(self):
160
        if self._match.group('prefix') == None:
161
            self._basic_category = Numbered._identifier(''.join(map(stringify, self._description)))
162
        else:
163
            self._basic_category = self._match.group('prefix')
164
        if self._basic_category not in self._doc.defined:
165
            define(self._basic_category, self._doc)
166
167
    def _compute_levels(self):
168
        # Compute the first and last section level values
169
        self._first_section_level = len(self._match.group('hidden')) // 2
170
        self._last_section_level = len(self._match.group('header')) // 2
171
172
        # Get the default first and last section level
173
        if self._first_section_level == 0 and self._last_section_level == 0:
174
            self._first_section_level = self._doc.defined[self._basic_category]['first-section-level']
175
            self._last_section_level = self._doc.defined[self._basic_category]['last-section-level']
176
177
    def _compute_section_number(self):
178
        self._section_number = '.'.join(map(str, self._doc.headers[:self._last_section_level]))
179
180
    def _compute_leading(self):
181
        # Compute the leading (composed of the section numbering and a dot)
182
        if self._last_section_level != 0:
183
            self._leading = self._section_number + '.'
184
        else:
185
            self._leading =  ''
186
187
    def _compute_category(self):
188
        self._category = self._basic_category + ':' + self._leading
189
190
        # Is it a new category?
191
        if self._category not in self._doc.count:
192
            self._doc.count[self._category] = 0
193
194
        self._doc.count[self._category] = self._doc.count[self._category] + 1
195
196
    def _compute_number(self):
197
        self._number = str(self._doc.count[self._category])
198
199
    def _compute_tag(self):
200
        # Determine the final tag
201
        if self._match.group('name') == None:
202
            self._tag = self._category + self._number
203
        else:
204
            self._tag = self._basic_category + ':' + self._match.group('name')
205
206
        # Compute collections
207
        if self._basic_category not in self._doc.collections:
208
            self._doc.collections[self._basic_category] = []
209
210
        self._doc.collections[self._basic_category].append(self._tag)
211
212
    def _compute_local_number(self):
213
        # Replace the '-.-.+.+...#' by the category count (omitting the hidden part)
214
        self._local_number = '.'.join(map(str, self._doc.headers[self._first_section_level:self._last_section_level] + [self._number]))
215
216
    def _compute_global_number(self):
217
        # Compute the global number
218
        if self._section_number:
219
            self._global_number = self._section_number + '.' + self._number
220
        else:
221
            self._global_number = self._number
222
223
    def _compute_data(self):
224
        classes = self._doc.defined[self._basic_category]['classes']
225
        self._set_content([Span(
226
            classes=['pandoc-numbering-text'] + classes,
227
            identifier=self._tag
228
        )])
229
        self._link.classes = self._link.classes + classes
230
        self._entry.classes = self._entry.classes + classes
231
232
        # Prepare the final data
233
        if self._title:
234
            self._get_content()[0].content = copy.deepcopy(self._doc.defined[self._basic_category]['format-text-title'])
235
            self._link.content = copy.deepcopy(self._doc.defined[self._basic_category]['format-link-title'])
236
            self._entry.content = copy.deepcopy(self._doc.defined[self._basic_category]['format-entry-title'])
237
            self._caption = self._doc.defined[self._basic_category]['format-caption-title']
238
        else:
239
            self._get_content()[0].content = copy.deepcopy(self._doc.defined[self._basic_category]['format-text-classic'])
240
            self._link.content = copy.deepcopy(self._doc.defined[self._basic_category]['format-link-classic'])
241
            self._entry.content = copy.deepcopy(self._doc.defined[self._basic_category]['format-entry-classic'])
242
            self._caption = self._doc.defined[self._basic_category]['format-caption-classic']
243
244
        # Compute caption (report replacing %c at the end since it is not known for the moment)
245
        title = stringify(Span(*self._title))
246
        description = stringify(Span(*self._description))
247
        self._caption = self._caption.replace('%t', title.lower())
248
        self._caption = self._caption.replace('%T', title)
249
        self._caption = self._caption.replace('%d', description.lower())
250
        self._caption = self._caption.replace('%D', description)
251
        self._caption = self._caption.replace('%s', self._section_number)
252
        self._caption = self._caption.replace('%g', self._global_number)
253
        self._caption = self._caption.replace('%n', self._local_number)
254
        self._caption = self._caption.replace('#', self._local_number)
255
        if self._doc.format == 'latex':
256
            self._caption = self._caption.replace('%p', '\\pageref{' + self._tag + '}')
257
258
        # Compute content
259
        replace_description(self._elem, self._description)
260
        replace_title(self._elem, self._title)
261
        replace_global_number(self._elem, self._global_number)
262
        replace_section_number(self._elem, self._section_number)
263
        replace_local_number(self._elem, self._local_number)
264
265
        ## Compute link
266
        replace_description(self._link, self._description)
267
        replace_title(self._link, self._title)
268
        replace_global_number(self._link, self._global_number)
269
        replace_section_number(self._link, self._section_number)
270
        replace_local_number(self._link, self._local_number)
271
        if self._doc.format == 'latex':
272
            replace_page_number(self._link, self._tag)
273
274
        # Compute entry
275
        replace_description(self._entry, self._description)
276
        replace_title(self._entry, self._title)
277
        replace_global_number(self._entry, self._global_number)
278
        replace_section_number(self._entry, self._section_number)
279
        replace_local_number(self._entry, self._local_number)
280
281
        # Finalize the content
282
        if self._doc.format == 'latex':
283
            self._get_content()[0].content.insert(0, RawInline('\\label{' + self._tag + '}', 'tex'))
284
285
            latex_category = re.sub('[^a-z]+', '', self._basic_category)
286
            latex = '\\phantomsection\\addcontentsline{' + \
287
                latex_category + \
288
                '}{' + \
289
                latex_category + \
290
                '}{\\protect\\numberline {' + \
291
                self._leading + \
292
                self._number + \
293
                '}{\ignorespaces ' + \
294
                to_latex(self._entry) + \
295
                '}}'
296
            self._get_content().insert(0, RawInline(latex, 'tex'))
297
298
def replace_description(where, description):
299
    where.walk(partial(replacing, search='%D', replace=copy.deepcopy(description)))
300
    where.walk(partial(replacing, search='%d', replace=list(item.walk(lowering) for item in copy.deepcopy(description))))
301
302
def replace_title(where, title):
303
    where.walk(partial(replacing, search='%T', replace=copy.deepcopy(title)))
304
    where.walk(partial(replacing, search='%t', replace=list(item.walk(lowering) for item in copy.deepcopy(title))))
305
306
def replace_section_number(where, section_number):
307
    where.walk(partial(replacing, search='%s', replace=[Str(section_number)]))
308
309
def replace_global_number(where, global_number):
310
    where.walk(partial(replacing, search='%g', replace=[Str(global_number)]))
311
312
def replace_local_number(where, local_number):
313
    where.walk(partial(replacing, search='%n', replace=[Str(local_number)]))
314
    where.walk(partial(replacing, search='#', replace=[Str(local_number)]))
315
316
def replace_page_number(where, tag):
317
    where.walk(partial(replacing, search='%p', replace=[RawInline('\\pageref{' + tag + '}', 'tex')]))
318
319
def replace_count(where, count):
320
    where.walk(partial(replacing, search='%c', replace=[Str(count)]))
321
322
def remove_useless_latex(elem, doc):
323
    if isinstance(elem, (BlockQuote, BulletList, Citation, Cite, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Header, HorizontalRule, Image, LineBlock, LineBreak, LineItem, ListItem, Note, Para, RawBlock, RawInline, SoftBreak, Table, TableCell, TableRow)):
324
        return []
325
326
def to_latex(elem):
327
    return convert_text(run_filters([remove_useless_latex], doc=Plain(elem)), input_format='panflute', output_format='latex', extra_args=['--no-highlight'])
328
329
def define(category, doc):
330
    doc.defined[category] = {
331
        'first-section-level':    0,
332
        'last-section-level':     0,
333
        'format-text-classic':    [Strong(Str('%D'), Space(), Str('%n'))],
334
        'format-text-title':      [Strong(Str('%D'), Space(), Str('%n')), Space(), Emph(Str('(%T)'))],
335
        'format-link-classic':    [Str('%D'), Space(), Str('%n')],
336
        'format-link-title':      [Str('%D'), Space(), Str('%n'), Space(), Str('(%T)')],
337
        'format-caption-classic': '%D %n',
338
        'format-caption-title':   '%D %n (%T)',
339
        'format-entry-title':     [Str('%T')],
340
        'classes':                [category],
341
        'cite-shortcut':          True,
342
        'listing-title':          None,
343
        'listing-unnumbered':     True,
344
        'listing-unlisted':       True,
345
        'entry-tab':              1.5,
346
        'entry-space':            2.3,
347
    }
348
    if doc.format == 'latex':
349
        doc.defined[category]['format-entry-classic'] = [Str('%D')]
350
        doc.defined[category]['entry-tab'] = 1.5
351
        doc.defined[category]['entry-space'] = 2.3
352
    else:
353
        doc.defined[category]['format-entry-classic'] = [Str('%D'), Space(), Str('%g')]
354
355
def lowering(elem, doc):
356
    if isinstance(elem, Str):
357
        elem.text = elem.text.lower()
358
359
def replacing(elem, doc, search=None, replace=None):
360
    if isinstance(elem, Str):
361
        prepare = elem.text.split(search)
362
        if len(prepare) > 1:
363
364
            text = []
365
366
            if prepare[0] != '':
367
                text.append(Str(prepare[0]))
368
369
            for string in prepare[1:]:
370
                text.extend(replace)
371
                if string != '':
372
                    text.append(Str(string))
373
374
            return text
375
376
    return [elem]
377
378
def numbering(elem, doc):
379
    if isinstance(elem, Header):
380
        update_header_numbers(elem, doc)
381
    elif isinstance(elem, (Para, DefinitionItem)):
382
        numbered = Numbered(elem, doc)
383
        if numbered.tag is not None:
384
            doc.information[numbered.tag] = numbered
385
386
def referencing(elem, doc):
387
    if isinstance(elem, Link):
388
        return referencing_link(elem, doc)
389
    elif isinstance(elem, Cite):
390
        return referencing_cite(elem, doc)
391
    elif isinstance(elem, Span) and elem.identifier in doc.information:
392
        replace_count(elem, str(doc.count[doc.information[elem.identifier].category]))
393
394
def referencing_link(elem, doc):
395
    match = re.match('^#(?P<tag>([a-zA-Z][\w:.-]*))$', elem.url)
396
    if match:
397
        tag = match.group('tag')
398
        if tag in doc.information:
399
            replace_title(elem, doc.information[tag].title)
400
            replace_description(elem, doc.information[tag].description)
401
            replace_global_number(elem, doc.information[tag].global_number)
402
            replace_section_number(elem, doc.information[tag].section_number)
403
            replace_local_number(elem, doc.information[tag].local_number)
404
            replace_count(elem, str(doc.count[doc.information[tag].category]))
405
            if doc.format == 'latex':
406
                replace_page_number(elem, tag)
407
408
            title = stringify(Span(*doc.information[tag].title))
409
            description = stringify(Span(*doc.information[tag].description))
410
            elem.title = elem.title.replace('%t', title.lower())
411
            elem.title = elem.title.replace('%T', title)
412
            elem.title = elem.title.replace('%d', description.lower())
413
            elem.title = elem.title.replace('%D', description)
414
            elem.title = elem.title.replace('%s', doc.information[tag].section_number)
415
            elem.title = elem.title.replace('%g', doc.information[tag].global_number)
416
            elem.title = elem.title.replace('%n', doc.information[tag].local_number)
417
            elem.title = elem.title.replace('#', doc.information[tag].local_number)
418
            elem.title = elem.title.replace('%c', str(doc.count[doc.information[tag].category]))
419
            if doc.format == 'latex':
420
                elem.title = elem.title.replace('%p', '\\pageref{' + tag + '}')
421
422
def referencing_cite(elem, doc):
423
    if len(elem.content) == 1 and isinstance(elem.content[0], Str):
424
        match = re.match('^(@(?P<tag>(?P<category>[a-zA-Z][\w.-]*):(([a-zA-Z][\w.-]*)|(\d*(\.\d*)*))))$', elem.content[0].text)
425
        if match:
426
            category = match.group('category')
427
            if category in doc.defined and doc.defined[category]['cite-shortcut']:
428
                # Deal with @prefix:name shortcut
429
                tag = match.group('tag')
430
                if tag in doc.information:
431
                    ret = Link(
432
                        doc.information[tag].link,
433
                        url = '#' + tag,
434
                        title = doc.information[tag].caption.replace('%c', str(doc.count[doc.information[tag].category]))
435
                    )
436
                    replace_count(ret, str(doc.count[doc.information[tag].category]))
437
                    return ret
438
439
440
def update_header_numbers(elem, doc):
441
    if 'unnumbered' not in elem.classes:
442
        doc.headers[elem.level - 1] = doc.headers[elem.level - 1] + 1
443
        for index in range(elem.level, 6):
444
            doc.headers[index] = 0
445
446
def prepare(doc):
447
    doc.headers = [0, 0, 0, 0, 0, 0]
448
    doc.information = {}
449
    doc.defined = {}
450
451
    if 'pandoc-numbering' in doc.metadata.content and isinstance(doc.metadata.content['pandoc-numbering'], MetaMap):
452
        for category, definition in doc.metadata.content['pandoc-numbering'].content.items():
453
            if isinstance(definition, MetaMap):
454
                add_definition(category, definition, doc)
455
456
    doc.count = {}
457
    doc.collections = {}
458
459
def add_definition(category, definition, doc):
460
    # Create the category with options by default
461
    define(category, doc)
462
463
    # Detect general options
464
    if 'general' in definition:
465
        meta_cite(category, definition['general'], doc.defined)
466
        meta_listing(category, definition['general'], doc.defined)
467
        meta_levels(category, definition['general'], doc.defined)
468
        meta_classes(category, definition['general'], doc.defined)
469
470
    # Detect LaTeX options
471
    if doc.format == 'latex':
472
        if 'latex' in definition:
473
            meta_format_text(category, definition['latex'], doc.defined)
474
            meta_format_link(category, definition['latex'], doc.defined)
475
            meta_format_caption(category, definition['latex'], doc.defined)
476
            meta_format_entry(category, definition['latex'], doc.defined)
477
            meta_entry_tab(category, definition['latex'], doc.defined)
478
            meta_entry_space(category, definition['latex'], doc.defined)
479
    # Detect standard options
480
    else:
481
        if 'standard' in definition:
482
            meta_format_text(category, definition['standard'], doc.defined)
483
            meta_format_link(category, definition['standard'], doc.defined)
484
            meta_format_caption(category, definition['standard'], doc.defined)
485
            meta_format_entry(category, definition['standard'], doc.defined)
486
487
def meta_cite(category, definition, defined):
488
    if 'cite-shortcut' in definition:
489
        if isinstance(definition['cite-shortcut'], MetaBool):
490
            defined[category]['cite-shortcut'] = definition['cite-shortcut'].boolean
491
        else:
492
            debug('[WARNING] pandoc-numbering: cite-shortcut is not correct for category ' + category)
493
494
def meta_listing(category, definition, defined):
495
    if 'listing-title' in definition:
496
        if isinstance(definition['listing-title'], MetaInlines):
497
            defined[category]['listing-title'] = definition['listing-title'].content
498
            # Detach from original parent
499
            defined[category]['listing-title'].parent = None
500
        else:
501
            debug('[WARNING] pandoc-numbering: listing-title is not correct for category ' + category)
502
    if 'listing-unnumbered' in definition:
503
        if isinstance(definition['listing-unnumbered'], MetaBool):
504
            defined[category]['listing-unnumbered'] = definition['listing-unnumbered'].boolean
505
        else:
506
            debug('[WARNING] pandoc-numbering: listing-unnumbered is not correct for category ' + category)
507
    if 'listing-unlisted' in definition:
508
        if isinstance(definition['listing-unlisted'], MetaBool):
509
            defined[category]['listing-unlisted'] = definition['listing-unlisted'].boolean
510
        else:
511
            debug('[WARNING] pandoc-numbering: listing-unlisted is not correct for category ' + category)
512
513 View Code Duplication
def meta_format_text(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
514
    if 'format-text-classic' in definition:
515
        if isinstance(definition['format-text-classic'], MetaInlines):
516
            defined[category]['format-text-classic'] = definition['format-text-classic'].content
517
            # Detach from original parent
518
            defined[category]['format-text-classic'].parent = None
519
        else:
520
            debug('[WARNING] pandoc-numbering: format-text-classic is not correct for category ' + category)
521
522
    if 'format-text-title' in definition:
523
        if isinstance(definition['format-text-title'], MetaInlines):
524
            defined[category]['format-text-title'] = definition['format-text-title'].content
525
            # Detach from original parent
526
            defined[category]['format-text-title'].parent = None
527
        else:
528
            debug('[WARNING] pandoc-numbering: format-text-title is not correct for category ' + category)
529
530 View Code Duplication
def meta_format_link(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
531
    if 'format-link-classic' in definition:
532
        if isinstance(definition['format-link-classic'], MetaInlines):
533
            defined[category]['format-link-classic'] = definition['format-link-classic'].content
534
            # Detach from original parent
535
            defined[category]['format-link-classic'].parent = None
536
        else:
537
            debug('[WARNING] pandoc-numbering: format-link-classic is not correct for category ' + category)
538
539
    if 'format-link-title' in definition:
540
        if isinstance(definition['format-link-title'], MetaInlines):
541
            defined[category]['format-link-title'] = definition['format-link-title'].content
542
            # Detach from original parent
543
            defined[category]['format-link-title'].parent = None
544
        else:
545
            debug('[WARNING] pandoc-numbering: format-link-title is not correct for category ' + category)
546
547
def meta_format_caption(category, definition, defined):
548
    if 'format-caption-classic' in definition:
549
        if isinstance(definition['format-caption-classic'], MetaInlines):
550
            defined[category]['format-caption-classic'] = stringify(definition['format-caption-classic'])
551
        else:
552
            debug('[WARNING] pandoc-numbering: format-caption-classic is not correct for category ' + category)
553
554
    if 'format-caption-title' in definition:
555
        if isinstance(definition['format-caption-title'], MetaInlines):
556
            defined[category]['format-caption-title'] = stringify(definition['format-caption-title'])
557
        else:
558
            debug('[WARNING] pandoc-numbering: format-caption-title is not correct for category ' + category)
559
560 View Code Duplication
def meta_format_entry(category, definition, defined):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
561
    if 'format-entry-classic' in definition:
562
        if isinstance(definition['format-entry-classic'], MetaInlines):
563
            defined[category]['format-entry-classic'] = definition['format-entry-classic'].content
564
            # Detach from original parent
565
            defined[category]['format-entry-classic'].parent = None
566
        else:
567
            debug('[WARNING] pandoc-numbering: format-entry-classic is not correct for category ' + category)
568
569
    if 'format-entry-title' in definition:
570
        if isinstance(definition['format-entry-title'], MetaInlines):
571
            defined[category]['format-entry-title'] = definition['format-entry-title'].content
572
            # Detach from original parent
573
            defined[category]['format-entry-title'].parent = None
574
        else:
575
            debug('[WARNING] pandoc-numbering: format-entry-title is not correct for category ' + category)
576
577
def meta_entry_tab(category, definition, defined):
578
    if 'entry-tab' in definition and isinstance(definition['entry-tab'], MetaString):
579
        # Get the tab
580
        try:
581
            tab = float(definition['entry-tab'].text)
582
            if tab > 0:
583
                defined[category]['entry-tab'] = tab
584
            else:
585
                debug('[WARNING] pandoc-numbering: entry-tab must be positive for category ' + category)
586
        except ValueError:
587
            debug('[WARNING] pandoc-numbering: entry-tab is not correct for category ' + category)
588
589
def meta_entry_space(category, definition, defined):
590
    if 'entry-space' in definition and isinstance(definition['entry-space'], MetaString):
591
        # Get the space
592
        try:
593
            space = float(definition['entry-space'].text)
594
            if space > 0:
595
                defined[category]['entry-space'] = space
596
            else:
597
                debug('[WARNING] pandoc-numbering: entry-space must be positive for category ' + category)
598
        except ValueError:
599
            debug('[WARNING] pandoc-numbering: entry-space is not correct for category ' + category)
600
601
def meta_levels(category, definition, defined):
602
    if 'sectioning-levels' in definition and isinstance(definition['sectioning-levels'], MetaInlines) and len(definition['sectioning-levels'].content) == 1:
603
        match = re.match(Numbered.header_regex, definition['sectioning-levels'].content[0].text)
604
        if match:
605
            # Compute the first and last levels section
606
            defined[category]['first-section-level'] =  len(match.group('hidden')) // 2
607
            defined[category]['last-section-level'] = len(match.group('header')) // 2
608
    if 'first-section-level' in definition and isinstance(definition['first-section-level'], MetaString):
609
        # Get the level
610
        try:
611
            level = int(definition['first-section-level'].text) - 1
612
            if level >= 0 and level <= 6 :
613
                defined[category]['first-section-level'] = level
614
            else:
615
                debug('[WARNING] pandoc-numbering: first-section-level must be positive or zero for category ' + category)
616
        except ValueError:
617
            debug('[WARNING] pandoc-numbering: first-section-level is not correct for category ' + category)
618
    if 'last-section-level' in definition and isinstance(definition['last-section-level'], MetaString):
619
        # Get the level
620
        try:
621
            level = int(definition['last-section-level'].text)
622
            if level >= 0 and level <= 6 :
623
                defined[category]['last-section-level'] = level
624
            else:
625
                debug('[WARNING] pandoc-numbering: last-section-level must be positive or zero for category ' + category)
626
        except ValueError:
627
            debug('[WARNING] pandoc-numbering: last-section-level is not correct for category ' + category)
628
629
def meta_classes(category, definition, defined):
630
    if 'classes' in definition and isinstance(definition['classes'], MetaList):
631
        classes = []
632
        for elt in definition['classes'].content:
633
            classes.append(stringify(elt))
634
        defined[category]['classes'] = classes
635
636
def finalize(doc):
637
    listings = []
638
    # Loop on all listings definition
639
    i = 0
640
    for category, definition in doc.defined.items():
641
        if definition['listing-title'] is not None:
642
            classes = ['pandoc-numbering-listing'] + definition['classes']
643
            if definition['listing-unnumbered']:
644
                classes.append('unnumbered')
645
            if definition['listing-unlisted']:
646
                classes.append('unlisted')
647
            doc.content.insert(i, Header(*definition['listing-title'], level=1, classes=classes))
648
            i = i + 1
649
650
            if doc.format == 'latex':
651
                table = table_latex(doc, category, definition)
652
            else:
653
                table = table_other(doc, category, definition)
654
655
            if table:
656
                doc.content.insert(i, table)
657
                i = i + 1
658
659
def table_other(doc, category, definition):
660
    if category in doc.collections:
661
        # Prepare the list
662
        elements = []
663
        # Loop on the collection
664
        for tag in doc.collections[category]:
665
            # Add an item to the list
666
            elements.append(ListItem(Plain(Link(doc.information[tag].entry, url='#' + tag))))
667
        # Return a bullet list
668
        return BulletList(*elements)
669
    else:
670
        return None
671
672
def table_latex(doc, category, definition):
673
    latex_category = re.sub('[^a-z]+', '', category)
674
    latex = [
675
        link_color(doc),
676
        '\\makeatletter',
677
        '\\newcommand*\\l@' + latex_category + '{\\@dottedtocline{1}{' + str(definition['entry-tab']) + 'em}{'+ str(definition['entry-space']) +'em}}',
678
        '\\@starttoc{' + latex_category + '}',
679
        '\\makeatother'
680
    ]
681
    # Return a RawBlock
682
    return RawBlock(''.join(latex), 'tex')
683
684
def link_color(doc):
685
    # Get the link color
686
    metadata = doc.get_metadata()
687
    if 'toccolor' in metadata:
688
        return '\\hypersetup{linkcolor=' + str(metadata['toccolor']) + '}'
689
    else:
690
        return '\\hypersetup{linkcolor=black}'
691
692
def main(doc = None):
693
    return run_filters([numbering, referencing], prepare = prepare, doc = doc, finalize = finalize)
694
695
if __name__ == '__main__':
696
    main()
697