diff --git a/IPython/nbconvert/exporters/exporter.py b/IPython/nbconvert/exporters/exporter.py index af9da29..f15cb9c 100755 --- a/IPython/nbconvert/exporters/exporter.py +++ b/IPython/nbconvert/exporters/exporter.py @@ -68,6 +68,7 @@ default_filters = { 'strip_math_space': filters.strip_math_space, 'wrap_text': filters.wrap_text, 'escape_latex': filters.escape_latex, + 'parse_citation': filters.parse_citation } #----------------------------------------------------------------------------- diff --git a/IPython/nbconvert/filters/__init__.py b/IPython/nbconvert/filters/__init__.py index 9751c94..87dd748 100755 --- a/IPython/nbconvert/filters/__init__.py +++ b/IPython/nbconvert/filters/__init__.py @@ -3,4 +3,5 @@ from .datatypefilter import * from .highlight import * from .latex import * from .markdown import * -from .strings import * \ No newline at end of file +from .strings import * +from .citation import * \ No newline at end of file diff --git a/IPython/nbconvert/filters/citation.py b/IPython/nbconvert/filters/citation.py new file mode 100644 index 0000000..e03cc25 --- /dev/null +++ b/IPython/nbconvert/filters/citation.py @@ -0,0 +1,70 @@ +"""Citation handling for LaTeX output.""" + +#----------------------------------------------------------------------------- +# Copyright (c) 2013, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + + +__all__ = ['parse_citation'] + + +def parse_citation(s): + """Parse citations in Markdown cells. + + This looks for HTML tags having a data attribute names `data-cite` + and replaces it by the call to LaTeX cite command. The tranformation + looks like this: + + `(Granger, 2013)` + + Becomes + + `\\cite{granger}` + + Any HTML tag can be used, which allows the citations to be formatted + in HTML in any manner. + """ + try: + from lxml import html + except ImportError: + return s + + tree = html.fragment_fromstring(s, create_parent='div') + _process_node_cite(tree) + s = html.tostring(tree) + s = s.lstrip('
') + s = s.rstrip('
') + return s + + +def _process_node_cite(node): + """Do the citation replacement as we walk the lxml tree.""" + + def _get(o, name): + value = getattr(o, name) + return '' if value is None else value + + if 'data-cite' in node.attrib: + cite = '\cite{%(ref)s}' % {'ref': node.attrib['data-cite']} + prev = node.getprevious() + if prev is not None: + prev.tail = _get(prev, 'tail') + cite + _get(node, 'tail') + else: + parent = node.getparent() + if parent is not None: + parent.text = _get(parent, 'text') + cite + _get(node, 'tail') + try: + node.getparent().remove(node) + except AttributeError: + pass + else: + for child in node: + _process_node_cite(child) diff --git a/IPython/nbconvert/filters/tests/test_citation.py b/IPython/nbconvert/filters/tests/test_citation.py new file mode 100644 index 0000000..7bb033b --- /dev/null +++ b/IPython/nbconvert/filters/tests/test_citation.py @@ -0,0 +1,54 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from ..citation import parse_citation + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- + +test_md = """ +# My Heading + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac magna non augue +porttitor scelerisque ac id diam Granger. Mauris elit +velit, lobortis sed interdum at, vestibulum vitae libero Perez. +Lorem ipsum dolor sit amet, consectetur adipiscing elit +Thomas. Quisque iaculis ligula ut ipsum mattis viverra. + +* One Jonathan. +* Two Matthias. +* Three Paul. +""" + +test_md_parsed = """ +# My Heading + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac magna non augue +porttitor scelerisque ac id diam \cite{granger}. Mauris elit +velit, lobortis sed interdum at, vestibulum vitae libero \cite{fperez}. +Lorem ipsum dolor sit amet, consectetur adipiscing elit +\cite{takluyver}. Quisque iaculis ligula ut ipsum mattis viverra. + +* One \cite{jdfreder}. +* Two \cite{carreau}. +* Three \cite{ivanov}. +""" + +def test_parse_citation(): + """Are citations parsed properly?""" + try: + import lxml + except ImportError: + assert test_md == parse_citation(test_md) + else: + assert test_md_parsed == parse_citation(test_md) diff --git a/IPython/nbconvert/templates/latex/latex_basic.tplx b/IPython/nbconvert/templates/latex/latex_basic.tplx index ff3303b..d8d6e68 100644 --- a/IPython/nbconvert/templates/latex/latex_basic.tplx +++ b/IPython/nbconvert/templates/latex/latex_basic.tplx @@ -103,11 +103,11 @@ it introduces a new line ((* endblock stream *)) ((* block markdowncell scoped *)) -((( cell.source | markdown2latex ))) +((( cell.source | parse_citation | markdown2latex ))) ((* endblock markdowncell *)) ((* block headingcell scoped -*)) -((( ('#' * cell.level + cell.source) | replace('\n', ' ') | markdown2latex ))) +((( ('#' * cell.level + cell.source) | replace('\n', ' ') | parse_citation | markdown2latex ))) ((* endblock headingcell *)) ((* block rawcell scoped *)) @@ -127,6 +127,10 @@ unknown type ((( cell.type ))) ((( super() ))) ((* block bodyEnd *)) + +((* block bibliography *)) +((* endblock bibliography *)) + \end{document} ((* endblock bodyEnd *)) ((* endblock body *)) diff --git a/IPython/nbconvert/templates/latex/sphinx.tplx b/IPython/nbconvert/templates/latex/sphinx.tplx index ee59ad8..80ee3fb 100644 --- a/IPython/nbconvert/templates/latex/sphinx.tplx +++ b/IPython/nbconvert/templates/latex/sphinx.tplx @@ -192,6 +192,9 @@ Note: For best display, use latex syntax highlighting. =)) \renewcommand{\indexname}{Index} \printindex + ((* block bibliography *)) + ((* endblock bibliography *)) + % End of document \end{document} ((* endblock bodyEnd *)) @@ -229,7 +232,7 @@ Note: For best display, use latex syntax highlighting. =)) in IPYNB file titles) do not make their way into latex. Sometimes this causes latex to barf. =)) ((*- endif -*)) - {((( cell.source | markdown2latex )))} + {((( cell.source | parse_citation | markdown2latex )))} ((*- endblock headingcell *)) %============================================================================== @@ -239,7 +242,7 @@ Note: For best display, use latex syntax highlighting. =)) % called since we know we want latex output. %============================================================================== ((*- block markdowncell scoped-*)) -((( cell.source | markdown2latex ))) +((( cell.source | parse_citation | markdown2latex ))) ((*- endblock markdowncell -*)) %==============================================================================