├── .gitignore
├── .travis.install_pandoc.sh
├── .travis.yml
├── README.md
├── autoref.md
├── image.png
├── internalreferences.py
├── ref.md
├── setup.py
├── spec.md
└── tests
├── spec.html
├── spec.html5
├── spec.json
├── spec.latex
├── spec.markdown
└── tests.py
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 | *.egg-info
4 |
--------------------------------------------------------------------------------
/.travis.install_pandoc.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | mkdir ~/pandoc
4 | wget https://github.com/jgm/pandoc/releases/download/1.17.0.2/pandoc-1.17.0.2-1-amd64.deb
5 | dpkg --extract pandoc-1.17.0.2-1-amd64.deb ~/pandoc/1.17
6 | export PATH=~/pandoc/$PANDOC/usr/bin/:$PATH
7 |
8 | echo $PANDOC
9 | type -p pandoc
10 | pandoc --version
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | sudo: no
4 |
5 | python:
6 | - 2.7
7 | - 3.5
8 |
9 | env:
10 | global:
11 | - PEP=false
12 | matrix:
13 | - PANDOC=1.17
14 |
15 | matrix:
16 | include:
17 | - python: 2.7
18 | env: PEP=true
19 |
20 | before_install:
21 | - if ! $PEP; then source .travis.install_pandoc.sh; fi
22 |
23 | install:
24 | - pip install pep8
25 | - pip install .
26 |
27 | script:
28 | - if $PEP; then pep8 internalreferences.py; fi
29 | - if ! $PEP; then nosetests; fi
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This pandoc filter implements an internal reference manager for
2 | pandoc, making it possible to reference images and sections that
3 | have attribute tags.
4 |
5 | **This filter uses the same `@` syntax as citations**, with priority
6 | given to internal references if there is a key clash.
7 |
8 | This filter allows both `\ref` and `\autoref` referencing styles to
9 | be used, toggled with a metadata switch.
10 |
11 | Supported output formats are latex, html, html5 and markdown. The
12 | markdown output format can be used to convert
13 | markdown-with-figure-attributes into currently valid pandoc
14 | markdown.
15 |
16 | This allows you to write something like this:
17 |
18 | ```markdown
19 | Look at @fig:thing.
20 |
21 | {#fig:thing}
22 | ```
23 |
24 | and get this markdown:
25 |
26 | ```markdown
27 | Look at [Figure 1](#fig:thing).
28 |
29 |
30 | 
31 |
32 |
33 | ```
34 |
35 | this latex:
36 |
37 | ```latex
38 | Look at \autoref{fig:thing}.
39 |
40 | \begin{figure}[htbp]
41 | \centering
42 | \includegraphics{image.png}
43 | \caption{some caption}
44 | \label{fig:thing}
45 | \end{figure}
46 | ```
47 |
48 | or this html:
49 |
50 | ```html
51 | Look at Figure 1 .
52 |
53 |
57 | ```
58 |
59 | For example input see the [spec] and for the output see [markdown],
60 | [html], [html5] or [latex].
61 |
62 | [spec]: spec.md
63 | [markdown]: tests/spec.markdown
64 | [html]: tests/spec.html
65 | [html5]: tests/spec.html5
66 | [latex]: tests/spec.latex
67 |
68 |
69 | ### Usage:
70 |
71 | ```bash
72 | pandoc spec.md --filter internal-references.py --to latex
73 | ```
74 |
75 | alternately you can install it and use the command line link:
76 |
77 | ```bash
78 | python setup.py install
79 | pandoc spec.md --filter internal-references --to latex
80 | ```
81 |
82 |
83 | Requires [pandocfilters] and [pandoc].
84 |
85 | [pandocfilters]: https://pypi.python.org/pypi/pandocfilters
86 | [pandoc]: http://johnmacfarlane.net/pandoc/
87 |
88 |
89 | ### How it works:
90 |
91 | In order to manage references we need to maintain some internal
92 | state that tracks the objects that can be referenced in the
93 | document. This is implemented with the `ReferenceManager`.
94 |
95 | `pandocfilters` contains a function `toJSONFilter` that passes a
96 | given function over the entire document syntax tree and interfaces
97 | with pandoc via stdin/stdout.
98 |
99 | However, we need to walk the document tree twice, once to capture
100 | all of the objects (figures, sections, whatever) and again to change
101 | all of the internal links to the appropriate output. This requires a
102 | modified `toJSONFilter` that accepts a list of functions to pass the
103 | tree through sequentially.
104 |
105 | It is easy to determine the type of a reference object as we go
106 | along (whether figure, section or whatever) on the first pass and we
107 | can use this information to let us choose the right text for
108 | internal links on the second pass. This allows us to avoid being
109 | constrained to ids like '#fig:somefigure' to indicate a figure.
110 |
111 |
112 | ### TODO:
113 |
114 | - [ ] allow switching off figure / section text replacement (perhaps
115 | using document metadata as the switch)
116 |
117 | - [x] implement implicit internal reference link syntax for links to
118 | figures, i.e. `[](#ref) == [#ref]`
119 |
120 |
121 | ### Contributing:
122 |
123 | Very welcome. Make sure you add appropriate tests and use verbose
124 | commit messages and pull requests. Explain what you are trying to
125 | do in English so that I don't have to work it out through the code.
126 |
--------------------------------------------------------------------------------
/autoref.md:
--------------------------------------------------------------------------------
1 | `@ref` | [Figure 1](#ref)
2 |
3 | `@ref(a)` | [Figure 1](#ref)(a)
4 |
5 | `[@ref]` | [Figure 1](#ref)
6 |
7 | `[@ref(a)]` | [Figure 1(a)](#ref)
8 |
9 | `@ref, @ref2` | [Figure 1](#ref), [Figure 2](#ref2)
10 |
11 | `[@ref; @ref2]` | Figures [1](#ref) and [2](#ref2)
12 |
13 | `[see @ref]` | [see Figure 1](#ref)
14 |
15 | `[see @ref(a)]`| [see Figure 1(a)](#ref)
16 |
17 |
18 | ![caption](){#ref}
19 |
20 | ![caption](){#ref2}
21 |
--------------------------------------------------------------------------------
/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaren/pandoc-reference-filter/9c6ac72f54e1d314c06ed4849c74dd6325db827d/image.png
--------------------------------------------------------------------------------
/internalreferences.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import re
3 | from collections import OrderedDict
4 |
5 | import pandocfilters as pf
6 |
7 | from pandocattributes import PandocAttributes
8 |
9 |
10 | def RawInline(format, string):
11 | """Overwrite pandocfilters RawInline so that html5
12 | and html raw output both use the html writer.
13 | """
14 | if format == 'html5':
15 | format = 'html'
16 | return pf.RawInline(format, string)
17 |
18 |
19 | def RawBlock(format, string):
20 | """Overwrite pandocfilters RawBlock so that html5
21 | and html raw output both use the html writer.
22 | """
23 | if format == 'html5':
24 | format = 'html'
25 | return pf.RawBlock(format, string)
26 |
27 |
28 | def isheader(key, value):
29 | return (key == 'Header')
30 |
31 |
32 | math_label = r'\\label{(.*?)}'
33 |
34 | def islabeledmath(key, value):
35 | return (key == 'Math' and re.search(math_label, value[1]))
36 |
37 |
38 | def isattr(string):
39 | return string.startswith('{') and string.endswith('}')
40 |
41 |
42 | # define a new Figure type - an image with attributes
43 | Figure = pf.elt('Figure', 3) # caption, target, attrs
44 |
45 |
46 | def isfigure(key, value):
47 | return (key == 'Para' and len(value) == 2 and value[0]['t'] == 'Image')
48 |
49 |
50 | def isattrfigure(key, value):
51 | return (key == 'Para'
52 | and value[0]['t'] == 'Image'
53 | and isattr(pf.stringify(value[1:])))
54 |
55 |
56 | def isdivfigure(key, value):
57 | """Matches images contained in a Div with 'figure' as a class."""
58 | return (key == 'Div' and 'figure' in value[0][1])
59 |
60 |
61 | def isFigure(key, value):
62 | return key == 'Figure'
63 |
64 |
65 | def create_pandoc_multilink(strings, refs):
66 | inlines = [[pf.Str(str(s))] for s in strings]
67 | targets = [(r, "") for r in refs]
68 | links = [pf.Link(inline, target)
69 | for inline, target in zip(inlines, targets)]
70 |
71 | return join_items(links)
72 |
73 |
74 | def create_latex_multilink(labels):
75 | links = ['\\ref{{{label}}}'.format(label=label) for label in labels]
76 | return join_items(links, call=str)
77 |
78 |
79 | def join_items(items, method='append', call=pf.Str):
80 | """Join the list of items together in the format
81 |
82 | 'item[0]' if len(items) == 1
83 | 'item[0] and item[1]' if len(items) == 2
84 | 'item[0], item[1] and item[2]' if len(items) == 3
85 |
86 | and so on.
87 | """
88 | out = []
89 | join_to_out = getattr(out, method)
90 |
91 | join_to_out(items[0])
92 |
93 | if len(items) == 1:
94 | return out
95 |
96 | for item in items[1: -1]:
97 | out.append(call(', '))
98 | join_to_out(item)
99 |
100 | out.append(call(' and '))
101 | join_to_out(items[-1])
102 |
103 | return out
104 |
105 |
106 | def create_figures(key, value, format, metadata):
107 | """Convert Images with attributes to Figures.
108 |
109 | Images are [caption, (filename, title)].
110 |
111 | Figures are [caption, (filename, title), attrs].
112 |
113 | This isn't a supported pandoc type, we just use it internally.
114 | """
115 | if isattrfigure(key, value):
116 | image = value[0]
117 | attr = PandocAttributes(pf.stringify(value[1:]), 'markdown')
118 | caption, target = image['c']
119 | return Figure(caption, target, attr.to_pandoc())
120 |
121 | elif isdivfigure(key, value):
122 | # use the first image inside
123 | attr, blocks = value
124 | images = [b['c'][0] for b in blocks if b['c'][0]['t'] == 'Image']
125 | image = images[0]
126 | caption, target = image['c']
127 | return Figure(caption, target, attr)
128 |
129 | else:
130 | return None
131 |
132 |
133 | class ReferenceManager(object):
134 | """Internal reference manager.
135 |
136 | Stores all referencable objects in the document, with a label
137 | and a type, then allows us to look up the object and type using
138 | a label.
139 |
140 | This means that we can determine the appropriate replacement
141 | text of any given internal reference (no need for e.g. 'fig:' at
142 | the start of labels).
143 | """
144 | figure_styles = {
145 | 'latex': (u'\n'
146 | '\\begin{{figure}}[htbp]\n'
147 | '\\centering\n'
148 | '\\includegraphics{{{filename}}}\n'
149 | '\\caption{star}{{{caption}}}\n'
150 | '\\label{{{attr.id}}}\n'
151 | '\\end{{figure}}\n'),
152 |
153 | 'html': (u'\n'
154 | '\n'
155 | '
'
156 | '
{fcaption}
\n'
157 | '
\n'),
158 |
159 | 'html5': (u'\n'
160 | '\n'
161 | ' \n'
162 | '{fcaption} \n'
163 | ' \n'),
164 |
165 | 'markdown': (u'\n'
166 | '\n'
167 | '\n'
168 | '\n'
169 | '
\n')}
170 |
171 | latex_multi_autolink = u'\\cref{{{labels}}}{post}'
172 |
173 | section_count = [0, 0, 0, 0, 0, 0]
174 | figure_count = 0
175 | fig_replacement_count = 0
176 | auto_fig_id = '___fig___[{}]'.format
177 | equation_count = 0
178 | references = {}
179 |
180 | formats = ('html', 'html5', 'markdown', 'latex')
181 |
182 | def __init__(self, autoref=True):
183 | if autoref:
184 | self.replacements = {'figure': 'Figure {}',
185 | 'section': 'Section {}',
186 | 'math': 'Equation {}'}
187 |
188 | self.multi_replacements = {'figure': 'Figures ',
189 | 'section': 'Sections ',
190 | 'math': 'Equations '}
191 | elif not autoref:
192 | self.replacements = {'figure': '{}',
193 | 'section': '{}',
194 | 'math': '{}'}
195 |
196 | self.multi_replacements = {'figure': '',
197 | 'section': '',
198 | 'math': ''}
199 |
200 | self.autoref = autoref
201 |
202 | def increment_section_count(self, header_level):
203 | """Changing the section count is dependent on the header level.
204 |
205 | When we add to the section count, we want to reset the
206 | count for all headers at a higher header level than that
207 | given, increment the count at the header level, and leave
208 | the same all lower levels.
209 | """
210 | self.section_count[header_level - 1] += 1
211 | for i, _ in enumerate(self.section_count[header_level:]):
212 | self.section_count[header_level + i] = 0
213 |
214 | def format_section_count(self, header_level):
215 | """Format the section count for a given header level,
216 | leaving off info from higher header levels,
217 |
218 | e.g. section_count = [1, 2, 4, 3]
219 | format_section_count(3) == '1.2.4'
220 | """
221 | return '.'.join(str(i) for i in self.section_count[:header_level])
222 |
223 | def consume_references(self, key, value, format, metadata):
224 | """Find all figures, sections and math in the document
225 | and append reference information to the reference state.
226 | """
227 | if isFigure(key, value):
228 | self.consume_figure(key, value, format, metadata)
229 | elif isheader(key, value):
230 | self.consume_section(key, value, format, metadata)
231 | elif islabeledmath(key, value):
232 | self.consume_math(key, value, format, metadata)
233 |
234 | def replace_references(self, key, value, format, metadata):
235 | """Find all figures, sections and equations that can be
236 | referenced in the document and replace them with format
237 | appropriate substitutions.
238 | """
239 | if isFigure(key, value):
240 | return self.figure_replacement(key, value, format, metadata)
241 | elif isheader(key, value):
242 | return self.section_replacement(key, value, format, metadata)
243 | elif islabeledmath(key, value):
244 | return self.math_replacement(key, value, format, metadata)
245 |
246 | def consume_figure(self, key, value, format, metadata):
247 | """If the key, value represents a figure, append reference
248 | data to internal state.
249 | """
250 | _caption, (filename, target), (id, classes, kvs) = value
251 | if 'unnumbered' in classes:
252 | return
253 | else:
254 | self.figure_count += 1
255 | id = id or self.auto_fig_id(self.figure_count)
256 | self.references[id] = {'type': 'figure',
257 | 'id': self.figure_count,
258 | 'label': id}
259 |
260 | def consume_section(self, key, value, format, metadata):
261 | """If the key, value represents a section, append reference
262 | data to internal state.
263 | """
264 | level, attr, text = value
265 | label, classes, kvs = attr
266 |
267 | if 'unnumbered' in classes:
268 | return
269 | else:
270 | self.increment_section_count(level)
271 | secn = self.format_section_count(level)
272 | self.references[label] = {'type': 'section',
273 | 'id': secn,
274 | 'label': label}
275 |
276 | def consume_math(self, key, value, format, metadata):
277 | """If the key, value represents math, append reference
278 | data to internal state.
279 | """
280 | self.equation_count += 1
281 | mathtype, math = value
282 | label, = re.search(math_label, math).groups()
283 | self.references[label] = {'type': 'math',
284 | 'id': self.equation_count,
285 | 'label': label}
286 |
287 | def figure_replacement(self, key, value, format, metadata):
288 | """Replace figures with appropriate representation.
289 |
290 | This works with Figure, which is our special type for images
291 | with attributes. This allows us to set an id in the attributes.
292 |
293 | The other way of doing it would be to pull out a '\label{(.*)}'
294 | from the caption of an Image and use that to update the references.
295 | """
296 | _caption, (filename, target), attrs = value
297 | caption = pf.stringify(_caption)
298 |
299 | attr = PandocAttributes(attrs)
300 |
301 | if 'unnumbered' in attr.classes:
302 | star = '*'
303 | fcaption = caption
304 | else:
305 | self.fig_replacement_count += 1
306 | if not attr.id:
307 | attr.id = self.auto_fig_id(self.fig_replacement_count)
308 |
309 | ref = self.references[attr.id]
310 | star = ''
311 | if caption:
312 | fcaption = u'Figure {n}: {caption}'.format(n=ref['id'],
313 | caption=caption)
314 | else:
315 | fcaption = u'Figure {n}'.format(n=ref['id'])
316 |
317 | if 'figure' not in attr.classes:
318 | attr.classes.insert(0, 'figure')
319 |
320 | if format in self.formats:
321 | figure = self.figure_styles[format].format(attr=attr,
322 | filename=filename,
323 | alt=fcaption,
324 | fcaption=fcaption,
325 | caption=caption,
326 | star=star).encode('utf-8')
327 |
328 | return RawBlock(format, figure)
329 |
330 | else:
331 | alt = [pf.Str(fcaption)]
332 | target = (filename, '')
333 | image = pf.Image(alt, target)
334 | figure = pf.Para([image])
335 | return pf.Div(attr.to_pandoc(), [figure])
336 |
337 | def section_replacement(self, key, value, format, metadata):
338 | """Replace sections with appropriate representation.
339 | """
340 | level, attr, text = value
341 | label, classes, kvs = attr
342 |
343 | if 'unnumbered' in classes:
344 | pretext = ''
345 | else:
346 | ref = self.references[label]
347 | pretext = '{}: '.format(ref['id'])
348 |
349 | pretext = [pf.Str(pretext)]
350 |
351 | if format in ('html', 'html5', 'markdown'):
352 | return pf.Header(level, attr, pretext + text)
353 |
354 | elif format == 'latex':
355 | # have to do this to get rid of hyperref
356 | return pf.Header(level, attr, text)
357 |
358 | else:
359 | return pf.Header(level, attr, pretext + text)
360 |
361 | def math_replacement(self, key, value, format, metadata):
362 | """Create our own links to equations instead of relying on
363 | mathjax.
364 |
365 | http://meta.math.stackexchange.com/questions/3764/equation-and-equation-is-the-same-for-me
366 | """
367 | mathtype, math = value
368 | label = re.findall(math_label, math)[-1]
369 |
370 | attr = PandocAttributes()
371 | attr.id = '#' + label
372 |
373 | if format == 'latex':
374 | return pf.Math(mathtype, math)
375 |
376 | else:
377 | return pf.Span(attr.to_pandoc(), [pf.Math(mathtype, math)])
378 |
379 | def convert_internal_refs(self, key, value, format, metadata):
380 | """Convert all internal links from '#blah' into format
381 | specified in self.replacements.
382 | """
383 | if key != 'Cite':
384 | return None
385 |
386 | citations, inlines = value
387 |
388 | if len(citations) > 1:
389 | '''
390 | Note: Need to check that *all* of the citations in a
391 | multicitation are in the reference list. If not, the citation
392 | is bibliographic, and we want LaTeX to handle it, so just
393 | return unmodified.
394 | '''
395 | for citation in citations:
396 | if citation['citationId'] not in self.references: return
397 | return self.convert_multiref(key, value, format, metadata)
398 |
399 | else:
400 | citation = citations[0]
401 |
402 | prefix = pf.stringify(citation['citationPrefix'])
403 | suffix = pf.stringify(citation['citationSuffix'])
404 |
405 | if prefix:
406 | prefix += ' '
407 |
408 | label = citation['citationId']
409 |
410 | if label not in self.references:
411 | return
412 |
413 | rtype = self.references[label]['type']
414 | n = self.references[label]['id']
415 | text = self.replacements[rtype].format(n)
416 |
417 | if format == 'latex' and self.autoref:
418 | link = u'{pre}\\autoref{{{label}}}{post}'.format(pre=prefix,
419 | label=label,
420 | post=suffix)
421 | return pf.RawInline('latex', link)
422 |
423 | elif format == 'latex' and not self.autoref:
424 | link = u'{pre}\\ref{{{label}}}{post}'.format(pre=prefix,
425 | label=label,
426 | post=suffix)
427 | return pf.RawInline('latex', link)
428 |
429 | else:
430 | link_text = '{}{}{}'.format(prefix, text, suffix)
431 | link = pf.Link([pf.Str(link_text)], ('#' + label, ''))
432 | return link
433 |
434 | def convert_multiref(self, key, value, format, metadata):
435 | """Convert all internal links from '#blah' into format
436 | specified in self.replacements.
437 | """
438 | citations, inlines = value
439 |
440 | labels = [citation['citationId'] for citation in citations]
441 |
442 | if format == 'latex' and self.autoref:
443 | link = self.latex_multi_autolink.format(pre='',
444 | post='',
445 | labels=','.join(labels))
446 | return RawInline('latex', link)
447 |
448 | elif format == 'latex' and not self.autoref:
449 | link = ''.join(create_latex_multilink(labels))
450 | return RawInline('latex', link)
451 |
452 | else:
453 | D = [self.references[label] for label in labels]
454 | # uniquely ordered types
455 | types = list(OrderedDict.fromkeys(d['type'] for d in D))
456 |
457 | links = []
458 |
459 | for t in set(types):
460 | n = [d['id'] for d in D if d['type'] == t]
461 | labels = ['#' + d['label'] for d in D if d['type'] == t]
462 | multi_link = create_pandoc_multilink(n, labels)
463 |
464 | if len(labels) == 1:
465 | multi_link.insert(0,
466 | pf.Str(self.replacements[t].format('')))
467 | else:
468 | multi_link.insert(0, pf.Str(self.multi_replacements[t]))
469 |
470 | links.append(multi_link)
471 |
472 | return join_items(links, method='extend')
473 |
474 | @property
475 | def reference_filter(self):
476 | return [create_figures,
477 | self.consume_references,
478 | self.replace_references,
479 | self.convert_internal_refs]
480 |
481 |
482 | def toJSONFilter(actions):
483 | """Modified from pandocfilters to accept a list of actions (to
484 | apply in series) as well as a single action.
485 |
486 | Converts an action into a filter that reads a JSON-formatted
487 | pandoc document from stdin, transforms it by walking the tree
488 | with the action, and returns a new JSON-formatted pandoc document
489 | to stdout.
490 |
491 | The argument is a function action(key, value, format, meta),
492 | where key is the type of the pandoc object (e.g. 'Str', 'Para'),
493 | value is the contents of the object (e.g. a string for 'Str',
494 | a list of inline elements for 'Para'), format is the target
495 | output format (which will be taken for the first command line
496 | argument if present), and meta is the document's metadata.
497 |
498 | If the function returns None, the object to which it applies
499 | will remain unchanged. If it returns an object, the object will
500 | be replaced. If it returns a list, the list will be spliced in to
501 | the list to which the target object belongs. (So, returning an
502 | empty list deletes the object.)
503 | """
504 | doc = pf.json.loads(pf.sys.stdin.read())
505 | if len(pf.sys.argv) > 1:
506 | format = pf.sys.argv[1]
507 | else:
508 | format = ""
509 |
510 | if type(actions) is type(toJSONFilter):
511 | altered = pf.walk(doc, actions, format, doc[0]['unMeta'])
512 | elif type(actions) is list:
513 | altered = doc
514 | for action in actions:
515 | altered = pf.walk(altered, action, format, doc[0]['unMeta'])
516 |
517 | pf.json.dump(altered, pf.sys.stdout)
518 |
519 |
520 | def main():
521 | doc = pf.json.loads(pf.sys.stdin.read())
522 | if len(pf.sys.argv) > 1:
523 | format = pf.sys.argv[1]
524 | else:
525 | format = ""
526 |
527 | metadata = doc[0]['unMeta']
528 | args = {k: v['c'] for k, v in metadata.items()}
529 | autoref = args.get('autoref', True)
530 |
531 | refmanager = ReferenceManager(autoref=autoref)
532 |
533 | altered = doc
534 | for action in refmanager.reference_filter:
535 | altered = pf.walk(altered, action, format, metadata)
536 |
537 | pf.json.dump(altered, pf.sys.stdout)
538 |
539 |
540 | if __name__ == '__main__':
541 | main()
542 |
--------------------------------------------------------------------------------
/ref.md:
--------------------------------------------------------------------------------
1 | `@ref` | @ref
2 |
3 | `@ref(a)` | @ref(a)
4 |
5 | `[@ref]` | [@ref]
6 |
7 | `[@ref(a)]` | [@ref(a)]
8 |
9 | `@ref; @ref2` | @ref; @ref2
10 |
11 | `[@ref; @ref2]` | [@ref; @ref2]
12 |
13 | `[figure @ref]` | [figure @ref]
14 |
15 | `[figure @ref(a)]`| [figure @ref(a)]
16 |
17 |
18 | ![caption](){#ref}
19 |
20 | ![caption](){#ref2}
21 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 |
4 | setup(
5 | name="pandoc-internal-references",
6 | version='0.5.1',
7 | description="Image attributes and internal referencing in markdown",
8 | py_modules=['internalreferences'],
9 | author="Aaron O'Leary",
10 | author_email='dev@aaren.me',
11 | license='BSD 2-Clause',
12 | url='https://github.com/aaren/pandoc-reference-filter',
13 | install_requires=['pandocfilters', 'pandoc-attributes'],
14 | entry_points={
15 | 'console_scripts': [
16 | 'internal-references = internalreferences:main',
17 | ],
18 | }
19 | )
20 |
--------------------------------------------------------------------------------
/spec.md:
--------------------------------------------------------------------------------
1 | ## Experiments with pandoc figures (ˈjuːnɪˌkəʊd!) {#sec:expt .class1 .class2 key=value}
2 |
3 | {#fig:attr .class1 .class2 key=value}
4 |
5 | Here is a reference to @fig:attr and here is one to @fig:attr2.
6 |
7 | Here is reference to the section called @sec:expt.
8 |
9 |
10 | 
11 |
12 | 
13 |
14 |
15 | Here is @eq:silly:
16 |
17 | $$
18 | 2 + 2 = 5
19 | \label{eq:silly}
20 | $$
21 |
22 | ## Unnumbered Section {-}
23 |
24 | {#fig:nonum -}
25 |
26 |
27 | ## Multiple references {-}
28 |
29 | We can refer to multiple things of the same type: [@fig:attr; @fig:attr2]
30 |
31 | Or to multiple things of mixed type: [@fig:attr; @fig:attr2;
32 | @sec:expt; @eq:silly]
33 |
34 | But if there are any missing keys, nothing will happen: [@fig:attr;
35 | @fig:idontexist]
36 |
--------------------------------------------------------------------------------
/tests/spec.html:
--------------------------------------------------------------------------------
1 | 0.1: Experiments with pandoc figures (ˈjuːnɪˌkəʊd!)
2 |
3 |
6 |
7 | Here is a reference to Figure 1 and here is one to Figure 2 .
8 | Here is reference to the section called Section 0.1 .
9 |
10 |
13 |
14 |
18 | Here is Equation 1 :
19 | \[
20 | 2 + 2 = 5
21 | \label{eq:silly}
22 | \]
23 | Unnumbered Section
24 |
25 |
28 |
29 | Multiple references
30 | We can refer to multiple things of the same type: Figures 1 and 2
31 | Or to multiple things of mixed type: Section 0.1 , Equation 1 and Figures 1 and 2
32 | But if there are any missing keys, nothing will happen: [@fig:attr; @fig:idontexist]
33 |
--------------------------------------------------------------------------------
/tests/spec.html5:
--------------------------------------------------------------------------------
1 | 0.1: Experiments with pandoc figures (ˈjuːnɪˌkəʊd!)
2 |
3 |
4 |
5 | Figure 1: a figure that can be referred to (ˈjuːnɪˌkəʊd!)
6 |
7 |
8 | Here is a reference to Figure 1 and here is one to Figure 2 .
9 | Here is reference to the section called Section 0.1 .
10 |
11 |
12 |
13 | Figure 2: another figure that can be referred to (ˈjuːnɪˌkəʊd!)
14 |
15 |
16 |
17 | figure with no attr (ˈjuːnɪˌkəʊd!)
18 |
19 | Here is Equation 1 :
20 | \[
21 | 2 + 2 = 5
22 | \label{eq:silly}
23 | \]
24 | Unnumbered Section
25 |
26 |
27 |
28 | no numbering here (ˈjuːnɪˌkəʊd!)
29 |
30 |
31 | Multiple references
32 | We can refer to multiple things of the same type: Figures 1 and 2
33 | Or to multiple things of mixed type: Section 0.1 , Equation 1 and Figures 1 and 2
34 | But if there are any missing keys, nothing will happen: [@fig:attr; @fig:idontexist]
35 |
--------------------------------------------------------------------------------
/tests/spec.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "unMeta": {}
4 | },
5 | [
6 | {
7 | "t": "Header",
8 | "c": [
9 | 2,
10 | [
11 | "sec:expt",
12 | [
13 | "class1",
14 | "class2"
15 | ],
16 | [
17 | [
18 | "key",
19 | "value"
20 | ]
21 | ]
22 | ],
23 | [
24 | {
25 | "t": "Str",
26 | "c": "0.1: "
27 | },
28 | {
29 | "t": "Str",
30 | "c": "Experiments"
31 | },
32 | {
33 | "t": "Space",
34 | "c": []
35 | },
36 | {
37 | "t": "Str",
38 | "c": "with"
39 | },
40 | {
41 | "t": "Space",
42 | "c": []
43 | },
44 | {
45 | "t": "Str",
46 | "c": "pandoc"
47 | },
48 | {
49 | "t": "Space",
50 | "c": []
51 | },
52 | {
53 | "t": "Str",
54 | "c": "figures"
55 | },
56 | {
57 | "t": "Space",
58 | "c": []
59 | },
60 | {
61 | "t": "Str",
62 | "c": "(ˈjuːnɪˌkəʊd!)"
63 | }
64 | ]
65 | ]
66 | },
67 | {
68 | "t": "Div",
69 | "c": [
70 | [
71 | "fig:attr",
72 | [
73 | "figure",
74 | "class1",
75 | "class2"
76 | ],
77 | [
78 | [
79 | "key",
80 | "value"
81 | ]
82 | ]
83 | ],
84 | [
85 | {
86 | "t": "Para",
87 | "c": [
88 | {
89 | "t": "Image",
90 | "c": [
91 | [
92 | {
93 | "t": "Str",
94 | "c": "Figure 1: a figure that can be referred to (ˈjuːnɪˌkəʊd!)"
95 | }
96 | ],
97 | [
98 | "image.png",
99 | ""
100 | ]
101 | ]
102 | }
103 | ]
104 | }
105 | ]
106 | ]
107 | },
108 | {
109 | "t": "Para",
110 | "c": [
111 | {
112 | "t": "Str",
113 | "c": "Here"
114 | },
115 | {
116 | "t": "Space",
117 | "c": []
118 | },
119 | {
120 | "t": "Str",
121 | "c": "is"
122 | },
123 | {
124 | "t": "Space",
125 | "c": []
126 | },
127 | {
128 | "t": "Str",
129 | "c": "a"
130 | },
131 | {
132 | "t": "Space",
133 | "c": []
134 | },
135 | {
136 | "t": "Str",
137 | "c": "reference"
138 | },
139 | {
140 | "t": "Space",
141 | "c": []
142 | },
143 | {
144 | "t": "Str",
145 | "c": "to"
146 | },
147 | {
148 | "t": "Space",
149 | "c": []
150 | },
151 | {
152 | "t": "Link",
153 | "c": [
154 | [
155 | {
156 | "t": "Str",
157 | "c": "Figure 1"
158 | }
159 | ],
160 | [
161 | "#fig:attr",
162 | ""
163 | ]
164 | ]
165 | },
166 | {
167 | "t": "Space",
168 | "c": []
169 | },
170 | {
171 | "t": "Str",
172 | "c": "and"
173 | },
174 | {
175 | "t": "Space",
176 | "c": []
177 | },
178 | {
179 | "t": "Str",
180 | "c": "here"
181 | },
182 | {
183 | "t": "Space",
184 | "c": []
185 | },
186 | {
187 | "t": "Str",
188 | "c": "is"
189 | },
190 | {
191 | "t": "Space",
192 | "c": []
193 | },
194 | {
195 | "t": "Str",
196 | "c": "one"
197 | },
198 | {
199 | "t": "Space",
200 | "c": []
201 | },
202 | {
203 | "t": "Str",
204 | "c": "to"
205 | },
206 | {
207 | "t": "Space",
208 | "c": []
209 | },
210 | {
211 | "t": "Link",
212 | "c": [
213 | [
214 | {
215 | "t": "Str",
216 | "c": "Figure 2"
217 | }
218 | ],
219 | [
220 | "#fig:attr2",
221 | ""
222 | ]
223 | ]
224 | },
225 | {
226 | "t": "Str",
227 | "c": "."
228 | }
229 | ]
230 | },
231 | {
232 | "t": "Para",
233 | "c": [
234 | {
235 | "t": "Str",
236 | "c": "Here"
237 | },
238 | {
239 | "t": "Space",
240 | "c": []
241 | },
242 | {
243 | "t": "Str",
244 | "c": "is"
245 | },
246 | {
247 | "t": "Space",
248 | "c": []
249 | },
250 | {
251 | "t": "Str",
252 | "c": "reference"
253 | },
254 | {
255 | "t": "Space",
256 | "c": []
257 | },
258 | {
259 | "t": "Str",
260 | "c": "to"
261 | },
262 | {
263 | "t": "Space",
264 | "c": []
265 | },
266 | {
267 | "t": "Str",
268 | "c": "the"
269 | },
270 | {
271 | "t": "Space",
272 | "c": []
273 | },
274 | {
275 | "t": "Str",
276 | "c": "section"
277 | },
278 | {
279 | "t": "Space",
280 | "c": []
281 | },
282 | {
283 | "t": "Str",
284 | "c": "called"
285 | },
286 | {
287 | "t": "Space",
288 | "c": []
289 | },
290 | {
291 | "t": "Link",
292 | "c": [
293 | [
294 | {
295 | "t": "Str",
296 | "c": "Section 0.1"
297 | }
298 | ],
299 | [
300 | "#sec:expt",
301 | ""
302 | ]
303 | ]
304 | },
305 | {
306 | "t": "Str",
307 | "c": "."
308 | }
309 | ]
310 | },
311 | {
312 | "t": "Div",
313 | "c": [
314 | [
315 | "fig:attr2",
316 | [
317 | "figure"
318 | ],
319 | []
320 | ],
321 | [
322 | {
323 | "t": "Para",
324 | "c": [
325 | {
326 | "t": "Image",
327 | "c": [
328 | [
329 | {
330 | "t": "Str",
331 | "c": "Figure 2: another figure that can be referred to (ˈjuːnɪˌkəʊd!)"
332 | }
333 | ],
334 | [
335 | "image.png",
336 | ""
337 | ]
338 | ]
339 | }
340 | ]
341 | }
342 | ]
343 | ]
344 | },
345 | {
346 | "t": "Para",
347 | "c": [
348 | {
349 | "t": "Image",
350 | "c": [
351 | [
352 | {
353 | "t": "Str",
354 | "c": "figure"
355 | },
356 | {
357 | "t": "Space",
358 | "c": []
359 | },
360 | {
361 | "t": "Str",
362 | "c": "with"
363 | },
364 | {
365 | "t": "Space",
366 | "c": []
367 | },
368 | {
369 | "t": "Str",
370 | "c": "no"
371 | },
372 | {
373 | "t": "Space",
374 | "c": []
375 | },
376 | {
377 | "t": "Str",
378 | "c": "attr"
379 | },
380 | {
381 | "t": "Space",
382 | "c": []
383 | },
384 | {
385 | "t": "Str",
386 | "c": "(ˈjuːnɪˌkəʊd!)"
387 | }
388 | ],
389 | [
390 | "image.png",
391 | "fig:"
392 | ]
393 | ]
394 | }
395 | ]
396 | },
397 | {
398 | "t": "Para",
399 | "c": [
400 | {
401 | "t": "Str",
402 | "c": "Here"
403 | },
404 | {
405 | "t": "Space",
406 | "c": []
407 | },
408 | {
409 | "t": "Str",
410 | "c": "is"
411 | },
412 | {
413 | "t": "Space",
414 | "c": []
415 | },
416 | {
417 | "t": "Link",
418 | "c": [
419 | [
420 | {
421 | "t": "Str",
422 | "c": "Equation 1"
423 | }
424 | ],
425 | [
426 | "#eq:silly",
427 | ""
428 | ]
429 | ]
430 | },
431 | {
432 | "t": "Str",
433 | "c": ":"
434 | }
435 | ]
436 | },
437 | {
438 | "t": "Para",
439 | "c": [
440 | {
441 | "t": "Span",
442 | "c": [
443 | [
444 | "#eq:silly",
445 | [],
446 | []
447 | ],
448 | [
449 | {
450 | "t": "Math",
451 | "c": [
452 | {
453 | "t": "DisplayMath",
454 | "c": []
455 | },
456 | "\n2 + 2 = 5\n\\label{eq:silly}\n"
457 | ]
458 | }
459 | ]
460 | ]
461 | }
462 | ]
463 | },
464 | {
465 | "t": "Header",
466 | "c": [
467 | 2,
468 | [
469 | "unnumbered-section",
470 | [
471 | "unnumbered"
472 | ],
473 | []
474 | ],
475 | [
476 | {
477 | "t": "Str",
478 | "c": ""
479 | },
480 | {
481 | "t": "Str",
482 | "c": "Unnumbered"
483 | },
484 | {
485 | "t": "Space",
486 | "c": []
487 | },
488 | {
489 | "t": "Str",
490 | "c": "Section"
491 | }
492 | ]
493 | ]
494 | },
495 | {
496 | "t": "Div",
497 | "c": [
498 | [
499 | "fig:nonum",
500 | [
501 | "figure",
502 | "unnumbered"
503 | ],
504 | []
505 | ],
506 | [
507 | {
508 | "t": "Para",
509 | "c": [
510 | {
511 | "t": "Image",
512 | "c": [
513 | [
514 | {
515 | "t": "Str",
516 | "c": "no numbering here (ˈjuːnɪˌkəʊd!)"
517 | }
518 | ],
519 | [
520 | "image.png",
521 | ""
522 | ]
523 | ]
524 | }
525 | ]
526 | }
527 | ]
528 | ]
529 | },
530 | {
531 | "t": "Header",
532 | "c": [
533 | 2,
534 | [
535 | "multiple-references",
536 | [
537 | "unnumbered"
538 | ],
539 | []
540 | ],
541 | [
542 | {
543 | "t": "Str",
544 | "c": ""
545 | },
546 | {
547 | "t": "Str",
548 | "c": "Multiple"
549 | },
550 | {
551 | "t": "Space",
552 | "c": []
553 | },
554 | {
555 | "t": "Str",
556 | "c": "references"
557 | }
558 | ]
559 | ]
560 | },
561 | {
562 | "t": "Para",
563 | "c": [
564 | {
565 | "t": "Str",
566 | "c": "We"
567 | },
568 | {
569 | "t": "Space",
570 | "c": []
571 | },
572 | {
573 | "t": "Str",
574 | "c": "can"
575 | },
576 | {
577 | "t": "Space",
578 | "c": []
579 | },
580 | {
581 | "t": "Str",
582 | "c": "refer"
583 | },
584 | {
585 | "t": "Space",
586 | "c": []
587 | },
588 | {
589 | "t": "Str",
590 | "c": "to"
591 | },
592 | {
593 | "t": "Space",
594 | "c": []
595 | },
596 | {
597 | "t": "Str",
598 | "c": "multiple"
599 | },
600 | {
601 | "t": "Space",
602 | "c": []
603 | },
604 | {
605 | "t": "Str",
606 | "c": "things"
607 | },
608 | {
609 | "t": "Space",
610 | "c": []
611 | },
612 | {
613 | "t": "Str",
614 | "c": "of"
615 | },
616 | {
617 | "t": "Space",
618 | "c": []
619 | },
620 | {
621 | "t": "Str",
622 | "c": "the"
623 | },
624 | {
625 | "t": "Space",
626 | "c": []
627 | },
628 | {
629 | "t": "Str",
630 | "c": "same"
631 | },
632 | {
633 | "t": "Space",
634 | "c": []
635 | },
636 | {
637 | "t": "Str",
638 | "c": "type:"
639 | },
640 | {
641 | "t": "Space",
642 | "c": []
643 | },
644 | {
645 | "t": "Str",
646 | "c": "Figures "
647 | },
648 | {
649 | "t": "Link",
650 | "c": [
651 | [
652 | {
653 | "t": "Str",
654 | "c": "1"
655 | }
656 | ],
657 | [
658 | "#fig:attr",
659 | ""
660 | ]
661 | ]
662 | },
663 | {
664 | "t": "Str",
665 | "c": " and "
666 | },
667 | {
668 | "t": "Link",
669 | "c": [
670 | [
671 | {
672 | "t": "Str",
673 | "c": "2"
674 | }
675 | ],
676 | [
677 | "#fig:attr2",
678 | ""
679 | ]
680 | ]
681 | }
682 | ]
683 | },
684 | {
685 | "t": "Para",
686 | "c": [
687 | {
688 | "t": "Str",
689 | "c": "Or"
690 | },
691 | {
692 | "t": "Space",
693 | "c": []
694 | },
695 | {
696 | "t": "Str",
697 | "c": "to"
698 | },
699 | {
700 | "t": "Space",
701 | "c": []
702 | },
703 | {
704 | "t": "Str",
705 | "c": "multiple"
706 | },
707 | {
708 | "t": "Space",
709 | "c": []
710 | },
711 | {
712 | "t": "Str",
713 | "c": "things"
714 | },
715 | {
716 | "t": "Space",
717 | "c": []
718 | },
719 | {
720 | "t": "Str",
721 | "c": "of"
722 | },
723 | {
724 | "t": "Space",
725 | "c": []
726 | },
727 | {
728 | "t": "Str",
729 | "c": "mixed"
730 | },
731 | {
732 | "t": "Space",
733 | "c": []
734 | },
735 | {
736 | "t": "Str",
737 | "c": "type:"
738 | },
739 | {
740 | "t": "Space",
741 | "c": []
742 | },
743 | {
744 | "t": "Str",
745 | "c": "Section "
746 | },
747 | {
748 | "t": "Link",
749 | "c": [
750 | [
751 | {
752 | "t": "Str",
753 | "c": "0.1"
754 | }
755 | ],
756 | [
757 | "#sec:expt",
758 | ""
759 | ]
760 | ]
761 | },
762 | {
763 | "t": "Str",
764 | "c": ", "
765 | },
766 | {
767 | "t": "Str",
768 | "c": "Equation "
769 | },
770 | {
771 | "t": "Link",
772 | "c": [
773 | [
774 | {
775 | "t": "Str",
776 | "c": "1"
777 | }
778 | ],
779 | [
780 | "#eq:silly",
781 | ""
782 | ]
783 | ]
784 | },
785 | {
786 | "t": "Str",
787 | "c": " and "
788 | },
789 | {
790 | "t": "Str",
791 | "c": "Figures "
792 | },
793 | {
794 | "t": "Link",
795 | "c": [
796 | [
797 | {
798 | "t": "Str",
799 | "c": "1"
800 | }
801 | ],
802 | [
803 | "#fig:attr",
804 | ""
805 | ]
806 | ]
807 | },
808 | {
809 | "t": "Str",
810 | "c": " and "
811 | },
812 | {
813 | "t": "Link",
814 | "c": [
815 | [
816 | {
817 | "t": "Str",
818 | "c": "2"
819 | }
820 | ],
821 | [
822 | "#fig:attr2",
823 | ""
824 | ]
825 | ]
826 | }
827 | ]
828 | },
829 | {
830 | "t": "Para",
831 | "c": [
832 | {
833 | "t": "Str",
834 | "c": "But"
835 | },
836 | {
837 | "t": "Space",
838 | "c": []
839 | },
840 | {
841 | "t": "Str",
842 | "c": "if"
843 | },
844 | {
845 | "t": "Space",
846 | "c": []
847 | },
848 | {
849 | "t": "Str",
850 | "c": "there"
851 | },
852 | {
853 | "t": "Space",
854 | "c": []
855 | },
856 | {
857 | "t": "Str",
858 | "c": "are"
859 | },
860 | {
861 | "t": "Space",
862 | "c": []
863 | },
864 | {
865 | "t": "Str",
866 | "c": "any"
867 | },
868 | {
869 | "t": "Space",
870 | "c": []
871 | },
872 | {
873 | "t": "Str",
874 | "c": "missing"
875 | },
876 | {
877 | "t": "Space",
878 | "c": []
879 | },
880 | {
881 | "t": "Str",
882 | "c": "keys,"
883 | },
884 | {
885 | "t": "Space",
886 | "c": []
887 | },
888 | {
889 | "t": "Str",
890 | "c": "nothing"
891 | },
892 | {
893 | "t": "Space",
894 | "c": []
895 | },
896 | {
897 | "t": "Str",
898 | "c": "will"
899 | },
900 | {
901 | "t": "Space",
902 | "c": []
903 | },
904 | {
905 | "t": "Str",
906 | "c": "happen:"
907 | },
908 | {
909 | "t": "Space",
910 | "c": []
911 | },
912 | {
913 | "t": "Cite",
914 | "c": [
915 | [
916 | {
917 | "citationSuffix": [],
918 | "citationNoteNum": 0,
919 | "citationMode": {
920 | "t": "NormalCitation",
921 | "c": []
922 | },
923 | "citationPrefix": [],
924 | "citationId": "fig:attr",
925 | "citationHash": 0
926 | },
927 | {
928 | "citationSuffix": [],
929 | "citationNoteNum": 0,
930 | "citationMode": {
931 | "t": "NormalCitation",
932 | "c": []
933 | },
934 | "citationPrefix": [],
935 | "citationId": "fig:idontexist",
936 | "citationHash": 0
937 | }
938 | ],
939 | [
940 | {
941 | "t": "Str",
942 | "c": "[@fig:attr;"
943 | },
944 | {
945 | "t": "Space",
946 | "c": []
947 | },
948 | {
949 | "t": "Str",
950 | "c": "@fig:idontexist]"
951 | }
952 | ]
953 | ]
954 | }
955 | ]
956 | }
957 | ]
958 | ]
959 |
--------------------------------------------------------------------------------
/tests/spec.latex:
--------------------------------------------------------------------------------
1 | \subsection{Experiments with pandoc figures
2 | (ˈjuːnɪˌkəʊd!)}\label{sec:expt}
3 |
4 |
5 | \begin{figure}[htbp]
6 | \centering
7 | \includegraphics{image.png}
8 | \caption{a figure that can be referred to (ˈjuːnɪˌkəʊd!)}
9 | \label{fig:attr}
10 | \end{figure}
11 |
12 | Here is a reference to \autoref{fig:attr} and here is one to
13 | \autoref{fig:attr2}.
14 |
15 | Here is reference to the section called \autoref{sec:expt}.
16 |
17 |
18 | \begin{figure}[htbp]
19 | \centering
20 | \includegraphics{image.png}
21 | \caption{another figure that can be referred to (ˈjuːnɪˌkəʊd!)}
22 | \label{fig:attr2}
23 | \end{figure}
24 |
25 | \begin{figure}[htbp]
26 | \centering
27 | \includegraphics{image.png}
28 | \caption{figure with no attr (ˈjuːnɪˌkəʊd!)}
29 | \end{figure}
30 |
31 | Here is \autoref{eq:silly}:
32 |
33 | \[
34 | 2 + 2 = 5
35 | \label{eq:silly}
36 | \]
37 |
38 | \subsection*{Unnumbered Section}\label{unnumbered-section}
39 | \addcontentsline{toc}{subsection}{Unnumbered Section}
40 |
41 |
42 | \begin{figure}[htbp]
43 | \centering
44 | \includegraphics{image.png}
45 | \caption*{no numbering here (ˈjuːnɪˌkəʊd!)}
46 | \label{fig:nonum}
47 | \end{figure}
48 |
49 | \subsection*{Multiple references}\label{multiple-references}
50 | \addcontentsline{toc}{subsection}{Multiple references}
51 |
52 | We can refer to multiple things of the same type:
53 | \cref{fig:attr,fig:attr2}
54 |
55 | Or to multiple things of mixed type:
56 | \cref{fig:attr,fig:attr2,sec:expt,eq:silly}
57 |
58 | But if there are any missing keys, nothing will happen: {[}@fig:attr;
59 | @fig:idontexist{]}
60 |
--------------------------------------------------------------------------------
/tests/spec.markdown:
--------------------------------------------------------------------------------
1 | 0.1: Experiments with pandoc figures (ˈjuːnɪˌkəʊd!) {#sec:expt .class1 .class2 key="value"}
2 | ---------------------------------------------------
3 |
4 |
5 |
6 | 
7 |
8 |
9 |
10 | Here is a reference to [Figure 1](#fig:attr) and here is one to
11 | [Figure 2](#fig:attr2).
12 |
13 | Here is reference to the section called [Section 0.1](#sec:expt).
14 |
15 |
16 |
17 | 
18 |
19 |
20 |
21 | 
22 |
23 | Here is [Equation 1](#eq:silly):
24 |
25 | $$
26 | 2 + 2 = 5
27 | \label{eq:silly}
28 | $$
29 |
30 | Unnumbered Section {#unnumbered-section .unnumbered}
31 | ------------------
32 |
33 |
34 |
35 | 
36 |
37 |
38 |
39 | Multiple references {#multiple-references .unnumbered}
40 | -------------------
41 |
42 | We can refer to multiple things of the same type:
43 | Figures [1](#fig:attr) and [2](#fig:attr2)
44 |
45 | Or to multiple things of mixed type:
46 | Section [0.1](#sec:expt), Equation [1](#eq:silly) and Figures [1](#fig:attr) and [2](#fig:attr2)
47 |
48 | But if there are any missing keys, nothing will happen:
49 | [@fig:attr; @fig:idontexist]
50 |
--------------------------------------------------------------------------------
/tests/tests.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import json
3 |
4 | import nose.tools as nt
5 |
6 | import internalreferences
7 |
8 |
9 | def test_attributes():
10 | attr_markdown = r"""{#identify .class1 .class2
11 | key1=blah key2="o'brien = 1" -}"""
12 | attr_dict = {'id': 'identify',
13 | 'classes': ['class1', 'class2', 'unnumbered'],
14 | 'key1': 'blah',
15 | 'key2': '"o\'brien = 1"'
16 | }
17 | attr_html = '''id="identify" class="class1 class2 unnumbered" key1=blah key2="o'brien = 1"'''
18 |
19 | attr = internalreferences.PandocAttributes(attr_markdown, 'markdown')
20 |
21 | print(attr_dict)
22 | print(attr.to_dict())
23 | nt.assert_dict_equal(attr_dict, attr.to_dict())
24 | nt.assert_equal(attr_html, attr.to_html())
25 |
26 |
27 | def call_pandoc(format):
28 | pandoc_cmd = ('pandoc', 'spec.md',
29 | '--filter', './internalreferences.py',
30 | '--mathjax',
31 | '--to', format)
32 | p = subprocess.Popen(pandoc_cmd, stdout=subprocess.PIPE)
33 | stdout, stderr = p.communicate()
34 | return stdout.decode()
35 |
36 |
37 | def _test(format):
38 | pandoc_output = call_pandoc(format)
39 |
40 | ref_file = 'tests/spec.{ext}'.format(ext=format)
41 |
42 | with open(ref_file) as f:
43 | nt.assert_multi_line_equal(pandoc_output, f.read())
44 |
45 |
46 | def test_markdown():
47 | _test('markdown')
48 |
49 |
50 | def test_html():
51 | _test('html')
52 |
53 |
54 | def test_html5():
55 | _test('html5')
56 |
57 |
58 | def test_latex():
59 | _test('latex')
60 |
61 |
62 | def test_generic():
63 | test = json.loads(call_pandoc('json'))
64 |
65 | with open('tests/spec.json') as f:
66 | ref = json.load(f)
67 |
68 | assert test == ref
69 |
70 |
71 | def test_all_formats():
72 | for format in ('markdown', 'latex', 'html', 'html5'):
73 | _test(format)
74 |
75 |
76 | if __name__ == '__main__':
77 | print("Comparing pandoc output with reference output in tests/spec.format")
78 | test_markdown()
79 | test_html()
80 | test_html5()
81 | test_latex()
82 | print("All comparison tests passed ok!")
83 |
--------------------------------------------------------------------------------