html.py
218 lines
| 7.4 KiB
| text/x-python
|
PythonLexer
/ converters / html.py
David Warde-Farley
|
r8789 | """Implements conversion to ordinary HTML output. | ||
This file implements a class that handles rendering IPython notebooks as | ||||
HTML, suitable for posting to the web. | ||||
Converters for more specific HTML generation needs (suitable for posting to | ||||
a particular web service) can usefully subclass `ConverterHTML` and override | ||||
certain methods. For output tuned to the Blogger blogging platform, see the | ||||
`ConverterBloggerHTML` class. | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (c) 2012, 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. | ||||
#----------------------------------------------------------------------------- | ||||
Matthias BUSSONNIER
|
r8620 | from __future__ import absolute_import | ||
David Warde-Farley
|
r8789 | # Stdlib imports | ||
import io | ||||
import os | ||||
# Third-party imports | ||||
from markdown import markdown | ||||
# IPython imports | ||||
from IPython.utils import path | ||||
# Our own imports | ||||
Anthony Scopatz
|
r8933 | from .base import Converter | ||
from .utils import text_cell, output_container | ||||
from .utils import highlight, coalesce_streams, ansi2html | ||||
Matthias BUSSONNIER
|
r8620 | |||
David Warde-Farley
|
r8749 | |||
David Warde-Farley
|
r8789 | #----------------------------------------------------------------------------- | ||
David Warde-Farley
|
r8808 | # Class declarations | ||
David Warde-Farley
|
r8789 | #----------------------------------------------------------------------------- | ||
David Warde-Farley
|
r8811 | |||
Matthias BUSSONNIER
|
r8618 | class ConverterHTML(Converter): | ||
David Warde-Farley
|
r8811 | #------------------------------------------------------------------------- | ||
# Class-level attributes determining the behaviour of the class but | ||||
# probably not varying from instance to instance. | ||||
#------------------------------------------------------------------------- | ||||
Matthias BUSSONNIER
|
r8618 | extension = 'html' | ||
Maximilian Albert
|
r8746 | blank_symbol = ' ' | ||
Matthias BUSSONNIER
|
r8618 | |||
def in_tag(self, tag, src, attrs=None): | ||||
"""Return a list of elements bracketed by the given tag""" | ||||
attr_s = '' if attrs is None else \ | ||||
David Warde-Farley
|
r8749 | ' '.join("%s=%s" % (attr, value) | ||
for attr, value in attrs.iteritems()) | ||||
Matthias BUSSONNIER
|
r8618 | return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag] | ||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | def _ansi_colored(self, text): | ||
return ['<pre>%s</pre>' % ansi2html(text)] | ||||
def _stylesheet(self, fname): | ||||
with io.open(fname, encoding='utf-8') as f: | ||||
s = f.read() | ||||
return self.in_tag('style', s, dict(type='"text/css"')) | ||||
def _out_prompt(self, output): | ||||
if output.output_type == 'pyout': | ||||
Maximilian Albert
|
r8746 | content = 'Out[%s]:' % self._get_prompt_number(output) | ||
Matthias BUSSONNIER
|
r8618 | else: | ||
content = '' | ||||
return ['<div class="prompt output_prompt">%s</div>' % content] | ||||
def header_body(self): | ||||
"""Return the body of the header as a list of strings.""" | ||||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | from pygments.formatters import HtmlFormatter | ||
header = [] | ||||
static = os.path.join(path.get_ipython_package_dir(), | ||||
'frontend', 'html', 'notebook', 'static', | ||||
) | ||||
here = os.path.split(os.path.realpath(__file__))[0] | ||||
css = os.path.join(static, 'css') | ||||
for sheet in [ | ||||
# do we need jquery and prettify? | ||||
David Warde-Farley
|
r8749 | # os.path.join(static, 'jquery', 'css', 'themes', 'base', | ||
# 'jquery-ui.min.css'), | ||||
Matthias BUSSONNIER
|
r8618 | # os.path.join(static, 'prettify', 'prettify.css'), | ||
os.path.join(css, 'boilerplate.css'), | ||||
os.path.join(css, 'fbm.css'), | ||||
os.path.join(css, 'notebook.css'), | ||||
os.path.join(css, 'renderedhtml.css'), | ||||
# our overrides: | ||||
David Warde-Farley
|
r8749 | os.path.join(here, '..', 'css', 'static_html.css'), | ||
Matthias BUSSONNIER
|
r8618 | ]: | ||
header.extend(self._stylesheet(sheet)) | ||||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | # pygments css | ||
pygments_css = HtmlFormatter().get_style_defs('.highlight') | ||||
header.extend(['<meta charset="UTF-8">']) | ||||
David Warde-Farley
|
r8749 | header.extend(self.in_tag('style', pygments_css, | ||
dict(type='"text/css"'))) | ||||
Matthias BUSSONNIER
|
r8618 | # TODO: this should be allowed to use local mathjax: | ||
David Warde-Farley
|
r8749 | header.extend(self.in_tag('script', '', {'type': '"text/javascript"', | ||
'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/' | ||||
'latest/MathJax.js?config=TeX-AMS_HTML"', | ||||
Matthias BUSSONNIER
|
r8618 | })) | ||
David Warde-Farley
|
r8749 | with io.open(os.path.join(here, '..', 'js', 'initmathjax.js'), | ||
Matthias BUSSONNIER
|
r8618 | encoding='utf-8') as f: | ||
David Warde-Farley
|
r8749 | header.extend(self.in_tag('script', f.read(), | ||
Matthias BUSSONNIER
|
r8618 | {'type': '"text/javascript"'})) | ||
return header | ||||
def optional_header(self): | ||||
return ['<html>', '<head>'] + self.header_body() + \ | ||||
['</head>', '<body>'] | ||||
def optional_footer(self): | ||||
return ['</body>', '</html>'] | ||||
@text_cell | ||||
def render_heading(self, cell): | ||||
marker = cell.level | ||||
return [u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)] | ||||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | def render_code(self, cell): | ||
if not cell.input: | ||||
return [] | ||||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | lines = ['<div class="cell border-box-sizing code_cell vbox">'] | ||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | lines.append('<div class="input hbox">') | ||
Maximilian Albert
|
r8746 | n = self._get_prompt_number(cell) | ||
David Warde-Farley
|
r8749 | lines.append( | ||
'<div class="prompt input_prompt">In [%s]:</div>' % n | ||||
) | ||||
Matthias BUSSONNIER
|
r8618 | lines.append('<div class="input_area box-flex1">') | ||
David Warde-Farley
|
r8778 | lines.append(highlight(cell.input) if self.highlight_source | ||
Ivan Djokic
|
r8777 | else cell.input) | ||
David Warde-Farley
|
r8749 | lines.append('</div>') # input_area | ||
lines.append('</div>') # input | ||||
Matthias BUSSONNIER
|
r8618 | if cell.outputs: | ||
lines.append('<div class="vbox output_wrapper">') | ||||
lines.append('<div class="output vbox">') | ||||
David Warde-Farley
|
r8749 | |||
Matthias BUSSONNIER
|
r8618 | for output in coalesce_streams(cell.outputs): | ||
conv_fn = self.dispatch(output.output_type) | ||||
lines.extend(conv_fn(output)) | ||||
David Warde-Farley
|
r8749 | |||
lines.append('</div>') # output | ||||
lines.append('</div>') # output_wrapper | ||||
lines.append('</div>') # cell | ||||
Matthias BUSSONNIER
|
r8618 | return lines | ||
@text_cell | ||||
def render_markdown(self, cell): | ||||
return [markdown(cell.source)] | ||||
def render_raw(self, cell): | ||||
if self.raw_as_verbatim: | ||||
return self.in_tag('pre', cell.source) | ||||
else: | ||||
return [cell.source] | ||||
@output_container | ||||
def render_pyout(self, output): | ||||
for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']: | ||||
if fmt in output: | ||||
conv_fn = self.dispatch_display_format(fmt) | ||||
return conv_fn(output) | ||||
return [] | ||||
render_display_data = render_pyout | ||||
@output_container | ||||
def render_stream(self, output): | ||||
return self._ansi_colored(output.text) | ||||
@output_container | ||||
def render_pyerr(self, output): | ||||
# Note: a traceback is a *list* of frames. | ||||
# lines = [] | ||||
David Warde-Farley
|
r8749 | |||
# stb = | ||||
Matthias BUSSONNIER
|
r8618 | return self._ansi_colored('\n'.join(output.traceback)) | ||
def _img_lines(self, img_file): | ||||
return ['<img src="%s">' % img_file, '</img>'] | ||||
def _unknown_lines(self, data): | ||||
return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data) | ||||
def render_display_format_png(self, output): | ||||
return ['<img src="data:image/png;base64,%s"></img>' % output.png] | ||||
def render_display_format_svg(self, output): | ||||
return [output.svg] | ||||
def render_display_format_jpeg(self, output): | ||||
return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg] | ||||
def render_display_format_text(self, output): | ||||
return self._ansi_colored(output.text) | ||||
def render_display_format_html(self, output): | ||||
return [output.html] | ||||
def render_display_format_latex(self, output): | ||||
return [output.latex] | ||||
def render_display_format_json(self, output): | ||||
# html ignores json | ||||
return [] | ||||
def render_display_format_javascript(self, output): | ||||
return [output.javascript] | ||||