Changing style of PDF-Latex output through IPython Notebook conversion

10,368

Solution 1

Like the OP, I'm not very happy with IPython 2 output from nbconvert. Since the converter no longer uses the Sphinx documentclass or the Sphinx preprocessing system, you can't use the SphinxTransformer calls on the nbconverter line.

The Rude-and-Crude Way

Drop --post PDF so nbconvert creates just the .tex file. Then, edit the .tex file to make it prettier. Then, run pdflatex on it a few times.

To make yourself the author, add a line like the following right after the \title line in the he .tex file:

\author{Never Saint}

You can find nice templates to help you make output look like what you want at latextemplates.com.

The Root-User's Way

Another approach is to roll a new template, starting with the ones in .../IPython/nbconvert/templates/latex. As the root user, add an article1.tplx file next to article.tplx and report.tplx. The following version creates a different output style that I personally find useful. The "margins" block produces front matter for LaTex, and the "predoc" block produces commands and text that are inserted at the start of the document. I blank out the "maketitle" block so there's no title page. Delete my empty "maketitle" block if you want to have a title page with an author and a date.

Usage: nbconvert --to latex yourNotebook.ipynb --template article1 --to PDF

% Default to the notebook output style
((* if not cell_style is defined *))
    ((* set cell_style = 'style_ipython.tplx' *))
((* endif *))

% Inherit from the specified cell style.
((* extends cell_style *))


%===============================================================================
% Latex article1, based on Article 
%===============================================================================

((* block docclass *))
\documentclass{article}
((* endblock docclass *))
((* block margins *))
\usepackage{blindtext}
\usepackage{mathptmx}% Times Roman font
\usepackage[scaled=.90]{helvet}
\usepackage{xcolor}
\usepackage{titlesec}
\titleformat{\title}[display]
  {\normalfont\sffamily\huge\bfseries\color{blue}}
  {\titlename}{20pt}{\Huge}
\titleformat{\section}
  {\normalfont\sffamily\Large\bfseries\color{darkgray}}
  {\subsection}{1em}{} 
\titleformat{\subsection}
  {\normalfont\sffamily\Large\bfseries\color{darkgray}}
  {\thesubsection}{1em}{} 
\parindent=0pt
\parskip=6pt
((* endblock margins *))

((* block predoc *))
\begin{document}

\pagestyle{plain}
\thispagestyle{plain}
\pagenumbering{arabic}
\setcounter{page}{5}
\lfoot{\copyright 2014}

This document is composed as an
\href{http://ipython.org/notebook.html}{IPython} notebook. The printed
version of this document was generated from the \texttt{IPython}
notebook, using the \texttt{ipython nbconvert} utility to generate a
\texttt{Latex} document. 
((* block maketitle *))((* endblock maketitle *))
((* block author *))\author{Put Your Name Here}((* endblock author *))
((* endblock predoc *))

Solution 2

Solving this problem is a real pain-in-the-butt. I also liked the iPython 0.x and 1.x styling. If you must have it, here's how you can do it.

Folks mentioned that you can create your own template. Well, iPython 1.x had perfectly good templates, so let's use them. I'm going to assume you have root on your machine, because we're going to hack our iPython's templates/latex directory.

# set for your python/ipython install
PYTHONLIB=/usr/lib64/python2.7/site-packages/
cd ${PYTHONLIB}/IPython/nbconvert/templates/latex

# download the files
for tplx in sphinx.tplx latex_article.tplx latex_book.tplx latex_basic.tplx; do
    wget https://raw.githubusercontent.com/ipython/ipython/1.x/IPython/nbconvert/templates/latex/${tplx} -O old_${tplx}
done

# update for some renaming that we just did
for tplx in old_latex_*.tplx; do
    sed -i 's|sphinx.tplx|old_sphinx.tplx|' ${tplx}
done

Now, the next step is to apply a patch to sphinx.tplx.

% cat sphinx_patch.patch
--- old_sphinx.tplx     2015-02-13 14:52:14.000000000 -0500
+++ mod_old_sphinx.tplx 2015-02-13 14:53:00.000000000 -0500
@@ -71,6 +71,7 @@
     % Pygments requirements
     \usepackage{fancyvrb}
     \usepackage{color}
+
     % ansi colors additions
     \definecolor{darkgreen}{rgb}{.12,.54,.11}
     \definecolor{lightgray}{gray}{.95}
@@ -83,6 +84,29 @@
     \definecolor{lightpurple}{rgb}{0.87,0.63,0.87}
     \definecolor{lightcyan}{rgb}{0.5,1.0,0.83}

+    %
+    % MEF NEW NEW MEF
+    %
+    \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
+    \newenvironment{Shaded}{}{}
+    \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
+    \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{{#1}}}
+    \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
+    \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
+    \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
+    \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
+    \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
+    \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{{#1}}}}
+    \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{{#1}}}
+    \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
+    \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{{#1}}}
+    \newcommand{\RegionMarkerTok}[1]{{#1}}
+    \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
+    \newcommand{\NormalTok}[1]{{#1}}
+    %
+    % MEF NEW NEW MEF
+    %
+    
     % Needed to box output/input
     \usepackage{tikz}
         \usetikzlibrary{calc,arrows,shadows}
@@ -374,7 +398,6 @@
 ((* if resources.sphinx.centeroutput *))
     \begin{center}
 ((* endif -*))
-((( output.latex | strip_math_space )))
 ((*- if resources.sphinx.centeroutput *))
     \end{center}
 ((* endif -*))

Cut and paste the above code to create the patch file. Then you can apply it with:

patch -p0 < sphinx_patch.patch

We're not done yet. Those templates need some support.

We'll do this back in the directory where the notebook you want to convert lives. We're going to create two python files. The first (I called it oldschool.py) does the preprocessing necessary to use the older templates. It is mostly scraped out of version 1.x iPython's nbconvert/transformers/sphinx.py and hacked to be a modern Preprocessor:

from __future__ import print_function, absolute_import
from IPython.nbconvert.preprocessors.base import Preprocessor
from IPython.utils.traitlets import Bool, Unicode
import os

class MySphinxyLatexPreprocessor(Preprocessor):
    interactive = Bool(False, config=True, help="""
        Allows you to define whether or not the Sphinx exporter will prompt
        you for input during the conversion process.  If this is set to false,
        the author, version, release, date, and chapter_style traits should
        be set.
        """)

    author = Unicode("Unknown Author", config=True, help="Author name")

    version = Unicode("", config=True, help="""
        Version number
        You can leave this blank if you do not want to render a version number.
        Example: "1.0.0"
        """)

    release = Unicode("", config=True, help="""
        Release name
        You can leave this blank if you do not want to render a release name.
        Example: "Rough Draft"
        """)

    publish_date = Unicode("", config=True, help="""
        Publish date
        This is the date to render on the document as the publish date.
        Leave this blank to default to todays date.  
        Example: "June 12, 1990"
        """)

    chapter_style = Unicode("Bjarne", config=True, help="""
        Sphinx chapter style
        This is the style to use for the chapter headers in the document.
        You may choose one of the following:
            "Bjarne"    (default)
            "Lenny"
            "Glenn"
            "Conny"
            "Rejne"
            "Sonny"    (used for international documents)
        """)

    output_style = Unicode("notebook", config=True, help="""
        Nbconvert Ipython
        notebook input/output formatting style.
        You may choose one of the following:
            "simple     (recommended for long code segments)"
            "notebook"  (default)
        """)

    center_output = Bool(False, config=True, help="""
        Optional attempt to center all output.  If this is false, no additional
        formatting is applied.
        """)

    use_headers = Bool(True, config=True, help="""
        Whether not a header should be added to the document.
        """)

    #Allow the user to override the title of the notebook (useful for
    #fancy document titles that the file system doesn't support.)
    overridetitle = Unicode("", config=True, help="")



    def preprocess(self, nb, resources):
        """
        Sphinx and LaTeX transformation to apply on each notebook.

        Parameters
        ----------
        nb : NotebookNode
            Notebook being converted
        resources : dictionary
            Additional resources used in the conversion process.  Allows
            transformers to pass variables into the Jinja engine.
        """
        # Generate Pygments definitions for Latex
        from pygments.formatters import LatexFormatter
        import sphinx
        sphinxPath = os.path.realpath(os.path.join(sphinx.package_dir,
                                                   "texinputs"))

        #
        # MEF:  set a latex resource
        # old from latex preprocessor
        # resources["latex"]["pygments_definitions"] = LatexFormatter(style='emacs').get_style_defs()
        # Generate Pygments definitions for Latex 
        # old from sphinx transformer
        # resources["sphinx"]["pygment_definitions"] = self._generate_pygments_latex_def()
        # the mashup:
        #resources["sphinx"]["pygment_definitions"] = \
        #         LatexFormatter(style='emacs').get_style_defs()
        # used below

        # MEF edit
        resources.setdefault("sphinx", {})
        #if not "sphinx" in resources:
        #    resources["sphinx"] = {}

        #
        # set sphinx resources
        #
        resources["sphinx"]["pygment_definitions"] = LatexFormatter().get_style_defs()
                                                   # LatexFormatter(style='emacs').get_style_defs()



        # Try to use the traitlets.
        resources["sphinx"]["author"] = self.author
        resources["sphinx"]["version"] = self.version
        resources["sphinx"]["release"] = self.release

        # Use todays date if none is provided.
        if self.publish_date:
            resources["sphinx"]["date"] = self.publish_date
        elif len(resources['metadata']['modified_date'].strip()) == 0:
            resources["sphinx"]["date"] = date.today().strftime(text.date_format)
        else:
            resources["sphinx"]["date"] = resources['metadata']['modified_date']

        # Sphinx traitlets.
        resources["sphinx"]["chapterstyle"] = self.chapter_style
        resources["sphinx"]["outputstyle"] = self.output_style
        resources["sphinx"]["centeroutput"] = self.center_output
        resources["sphinx"]["header"] = self.use_headers

        # Find and pass in the path to the Sphinx dependencies.
        resources["sphinx"]["texinputs"] = sphinxPath


        #
        # MEF edit
        #
        if self.overridetitle and len(self.overridetitle.strip()):
                resources['metadata']['name'] = self.overridetitle.strip()

        return nb, resources

The last file is easy ({config.py}).

c = get_config()
c.Exporter.preprocessors = ['oldschool.MySphinxyLatexPreprocessor']

Now, at the command line, you can do:

ipython nbconvert example.ipynb --to latex --config config.py --template old_latex_article.tplx --post PDF
ipython nbconvert example.ipynb --to latex --config config.py --template old_latex_book.tplx --post PDF

If you want to use basic (aka, {the old_latex_basic.tplx} file), you'll have to hack in the the main block of code we added to sphinx.tplx (the portion between the % MEF NEW NEW MEF comments).

Share:
10,368
Danf
Author by

Danf

Updated on June 14, 2022

Comments

  • Danf
    Danf almost 2 years

    Currently with the following command of my .ipynb file:

    $ ipython nbconvert --to latex --post PDF Untitled1.ipynb --SphinxTransformer.author="John Doe"
    [NbConvertApp] Using existing profile dir: u'/Users/me-macmini/.ipython/profile_default'
    [NbConvertApp] Converting notebook Untitled1.ipynb to latex
    [NbConvertApp] Support files will be in Untitled1_files/
    [NbConvertApp] Loaded template article.tplx
    [NbConvertApp] Writing 12876 bytes to Untitled1.tex
    [NbConvertApp] Building PDF
    [NbConvertApp] Running pdflatex 3 times: [u'pdflatex', u'Untitled1.tex']
    [NbConvertApp] Running bibtex 1 time: [u'bibtex', u'Untitled1']
    [NbConvertApp] WARNING | bibtex had problems, most likely because there were no citations
    [NbConvertApp] Removing temporary LaTeX files
    [NbConvertApp] PDF successfully created
    

    With IPython 2.1, I got the latex file that are formatted with the standard classical style:

    enter image description here

    My questions are:

    1. What should I do so that I can get the following style, right from ipython command? enter image description here

    2. Why the above command doesn't enable the author to appear?