mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add math support to Sphinx.
This commit is contained in:
parent
b7b483d01f
commit
5941814c4a
35
CHANGES
35
CHANGES
@ -13,29 +13,34 @@ New features added
|
||||
have Sphinx documentation, which will build the docs and place them
|
||||
in the standard distutils build directory.
|
||||
|
||||
* `SerializingHTMLBuilder` was added as new abstract builder that can
|
||||
be subclassed to serialize build HTML in a specific format. The
|
||||
`PickleHTMLBuilder` is a concrete subclass of it that uses pickle as
|
||||
serialization implementation.
|
||||
* ``SerializingHTMLBuilder`` was added as new abstract builder that
|
||||
can be subclassed to serialize build HTML in a specific format.
|
||||
The ``PickleHTMLBuilder`` is a concrete subclass of it that uses
|
||||
pickle as serialization implementation.
|
||||
|
||||
* `JSONHTMLBuilder` was added that similarily to `PickleHTMLBuilder`
|
||||
dumps the generated HTML into JSON files for further processing.
|
||||
* ``JSONHTMLBuilder`` was added as another ``SerializingHTMLBuilder``
|
||||
subclass that dumps the generated HTML into JSON files for further
|
||||
processing.
|
||||
|
||||
* The `automodule` directive now supports the ``synopsis``,
|
||||
``deprecated`` and ``platform`` options.
|
||||
|
||||
* The HTML builders have two additional attributes now that can be used
|
||||
to disable the anchor-link creation after headlines and definition
|
||||
links.
|
||||
* The HTML builders have two additional attributes now that can be
|
||||
used to disable the anchor-link creation after headlines and
|
||||
definition links.
|
||||
|
||||
* ``Sphinx.add_node()`` now takes optional visitor methods for the HTML,
|
||||
LaTeX and text translators; this prevents having to manually patch
|
||||
the classes.
|
||||
* sphinx.doc.autodoc has a new event ``autodoc-process-signature``
|
||||
that allows tuning function signature introspection.
|
||||
|
||||
* sphinx.doc.autodoc has a new event ``autodoc-process-signature`` that
|
||||
allows tuning function signature introspection.
|
||||
* ``Sphinx.add_node()`` now takes optional visitor methods for the
|
||||
HTML, LaTeX and text translators; this prevents having to manually
|
||||
patch the classes.
|
||||
|
||||
* Added new events: ``env-updated`` and ``missing-reference``.
|
||||
* Added ``Sphinx.add_javascript()`` that adds scripts to load in the
|
||||
default HTML template.
|
||||
|
||||
* Added new events: ``env-updated``, ``missing-reference``,
|
||||
``build-finished``.
|
||||
|
||||
|
||||
Release 0.4.2 (Jul 29, 2008)
|
||||
|
@ -13,8 +13,30 @@ Since the reST source files can have different extensions (some people like
|
||||
:confval:`source_suffix`) and different OSes have different path separators,
|
||||
Sphinx abstracts them: all "document names" are relative to the :term:`source
|
||||
directory`, the extension is stripped, and path separators are converted to
|
||||
slashes. All values, parameters and suchlike referring to "documents" expect
|
||||
such a document name.
|
||||
slashes :eq:`abc`. All values, parameters and suchlike referring to "documents" expect
|
||||
such a document :math:`name\sqrt{3}`.
|
||||
|
||||
.. math::
|
||||
:label: abc
|
||||
|
||||
3x+4y \sqrt{4}
|
||||
|
||||
x+y \\
|
||||
z+z
|
||||
|
||||
.. math:: a+b
|
||||
|
||||
c + d & = 5 \\
|
||||
& = 6
|
||||
|
||||
.. math:: 5x + 6
|
||||
|
||||
.. math::
|
||||
:label: def
|
||||
|
||||
test
|
||||
|
||||
See :eq:`def`.
|
||||
|
||||
|
||||
The TOC tree
|
||||
|
@ -21,7 +21,9 @@ import sys, os, re
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.pngmath']
|
||||
#pngmath_use_preview = True
|
||||
jsmath_path = '/home/gbr/jsMath/easy/load.js'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
@ -150,6 +150,14 @@ the following public API:
|
||||
Add the standard docutils :class:`Transform` subclass *transform* to the list
|
||||
of transforms that are applied after Sphinx parses a reST document.
|
||||
|
||||
.. method:: Sphinx.add_javascript(filename)
|
||||
|
||||
Add *filename* to the list of JavaScript files that the default HTML template
|
||||
will include. The filename must be relative to the HTML static path, see
|
||||
:confval:`the docs for the config value <html_static_path>`.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
.. method:: Sphinx.connect(event, callback)
|
||||
|
||||
Register *callback* to be called when *event* is emitted. For details on
|
||||
@ -254,6 +262,17 @@ registered event handlers.
|
||||
|
||||
.. versionadded:: 0.4
|
||||
|
||||
.. event:: build-finished (app, exception)
|
||||
|
||||
Emitted when a build has finished, before Sphinx exits, usually used for
|
||||
cleanup. This event is emitted even when the build process raised an
|
||||
exception, given as the *exception* argument. The exception is reraised in
|
||||
the application after the event handlers have run. If the build process
|
||||
raised no exception, *exception* will be ``None``. This allows to customize
|
||||
cleanup actions depending on the exception status.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
|
||||
.. _template-bridge:
|
||||
|
||||
|
166
doc/ext/math.rst
Normal file
166
doc/ext/math.rst
Normal file
@ -0,0 +1,166 @@
|
||||
.. highlight:: rest
|
||||
|
||||
Math support in Sphinx
|
||||
======================
|
||||
|
||||
.. module:: sphinx.ext.mathbase
|
||||
:synopsis: Common math support for pngmath and jsmath.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
Since mathematical notation isn't natively supported by HTML in any way, Sphinx
|
||||
supports math in documentation with two extensions.
|
||||
|
||||
The basic math support that is common to both extensions is contained in
|
||||
:mod:`sphinx.ext.mathbase`. Other math support extensions should, if possible,
|
||||
reuse that support too.
|
||||
|
||||
The input language for mathematics is LaTeX markup. This is the de-facto
|
||||
standard for plain-text math notation and has the added advantage that no
|
||||
further translation is necessary when building LaTeX output.
|
||||
|
||||
:mod:`mathbase` defines these new markup elements:
|
||||
|
||||
.. role:: math
|
||||
|
||||
Role for inline math. Use like this::
|
||||
|
||||
Since Pythagoras, we know that :math:`a^2 + b^2 = c^2`.
|
||||
|
||||
.. directive:: math
|
||||
|
||||
Directive for displayed math (math that takes the whole line for itself).
|
||||
|
||||
The directive supports multiple equations, which should be separated by a
|
||||
blank line::
|
||||
|
||||
.. math::
|
||||
|
||||
(a + b)^2 = a^2 + 2ab + b^2
|
||||
|
||||
(a - b)^2 = a^2 - 2ab + b^2
|
||||
|
||||
In addition, each single equation is set within a ``split`` environment,
|
||||
which means that you can have multiple aligned lines in an equation,
|
||||
aligned at ``&`` and separated by ``\\``::
|
||||
|
||||
.. math::
|
||||
|
||||
(a + b)^2 &= (a + b)(a + b) \\
|
||||
&= a^2 + 2ab + b^2
|
||||
|
||||
For more details, look into the documentation of the `AmSMath LaTeX
|
||||
package`_.
|
||||
|
||||
When the math is only one line of text, it can also be given as a directive
|
||||
argument::
|
||||
|
||||
.. math:: (a + b)^2 = a^2 + 2ab + b^2
|
||||
|
||||
Normally, equations are not numbered. If you want your equation to get a
|
||||
number, use the ``label`` option. When given, it selects a label for the
|
||||
equation, by which it can be cross-referenced, and causes an equation number
|
||||
to be issued. See :role:`eqref` for an example. The numbering style depends
|
||||
on the output format.
|
||||
|
||||
.. role:: eqref
|
||||
|
||||
Role for cross-referencing equations via their label. This currently works
|
||||
only within the same document. Example::
|
||||
|
||||
.. math:: e^{i\pi} + 1 = 0
|
||||
:label: euler
|
||||
|
||||
Euler's identity, equation :eqref:`euler`, was elected one of the most
|
||||
beautiful mathematical formulas.
|
||||
|
||||
|
||||
:mod:`sphinx.ext.pngmath` -- Render math as PNG images
|
||||
------------------------------------------------------
|
||||
|
||||
.. module:: sphinx.ext.pngmath
|
||||
:synopsis: Render math as PNG images.
|
||||
|
||||
This extension renders math via LaTeX and dvipng_ into PNG images. This of
|
||||
course means that the computer where the docs are built must have both programs
|
||||
available.
|
||||
|
||||
There are various config values you can set to influence how the images are built:
|
||||
|
||||
.. confval:: pngmath_latex
|
||||
|
||||
The command name with which to invoke LaTeX. The default is ``'latex'``; you
|
||||
may need to set this to a full path if ``latex`` not in the executable search
|
||||
path.
|
||||
|
||||
Since this setting is not portable from system to system, it is normally not
|
||||
useful to set it in ``conf.py``; rather, giving it on the
|
||||
:program:`sphinx-build` command line via the :option:`-D` option should be
|
||||
preferable, like this::
|
||||
|
||||
sphinx-build -b html -D pngmath_latex=C:\tex\latex.exe . _build/html
|
||||
|
||||
.. confval:: pngmath_dvipng
|
||||
|
||||
The command name with which to invoke ``dvipng``. The default is
|
||||
``'dvipng'``; you may need to set this to a full path if ``dvipng`` is not in
|
||||
the executable search path.
|
||||
|
||||
.. confval:: pngmath_latex_preamble
|
||||
|
||||
Additional LaTeX code to put into the preamble of the short LaTeX files that
|
||||
are used to translate the math snippets. This is empty by default. Use it
|
||||
e.g. to add more packages whose commands you want to use in the math.
|
||||
|
||||
.. confval:: pngmath_dvipng_args
|
||||
|
||||
Additional arguments to give to dvipng, as a list. This is empty by default.
|
||||
Arguments you might want to add here are e.g. ``['-bg', 'Transparent']``,
|
||||
which produces PNGs with a transparent background. This is not enabled by
|
||||
default because some Internet Explorer versions don't like transparent PNGs.
|
||||
|
||||
.. confval:: pngmath_use_preview
|
||||
|
||||
``dvipng`` has the ability to determine the "depth" of the rendered text: for
|
||||
example, when typesetting a fraction inline, the baseline of surrounding text
|
||||
should not be flush with the bottom of the image, rather the image should
|
||||
extend a bit below the baseline. This is what TeX calls "depth". When this
|
||||
is enabled, the images put into the HTML document will get a
|
||||
``vertical-align`` style that correctly aligns the baselines.
|
||||
|
||||
Unfortunately, this only works when the `preview-latex package`_ is
|
||||
installed. Therefore, the default for this option is ``False``.
|
||||
|
||||
|
||||
:mod:`sphinx.ext.jsmath` -- Render math via JavaScript
|
||||
------------------------------------------------------
|
||||
|
||||
.. module:: sphinx.ext.jsmath
|
||||
:synopsis: Render math via JavaScript.
|
||||
|
||||
This extension puts math as-is into the HTML files. The JavaScript package
|
||||
jsMath_ is then loaded and transforms the LaTeX markup to readable math live in
|
||||
the browser.
|
||||
|
||||
Because jsMath (and the necessary fonts) is very large, it is not included in
|
||||
Sphinx. You must install it yourself, and give Sphinx its path in this config
|
||||
value:
|
||||
|
||||
.. confval:: jsmath_path
|
||||
|
||||
The path to the JavaScript file to include in the HTML files in order to load
|
||||
JSMath. There is no default.
|
||||
|
||||
The path can be absolute or relative; if it is relative, it is relative to
|
||||
the root of the built docs.
|
||||
|
||||
For example, if you put JSMath into the static path of the Sphinx docs, this
|
||||
value would be ``_static/jsMath/easy/load.js``. If you host more than one
|
||||
Sphinx documentation set on one server, it is advisable to install jsMath in
|
||||
a shared location.
|
||||
|
||||
|
||||
.. _dvipng: http://savannah.nongnu.org/projects/dvipng/
|
||||
.. _jsMath: http://www.math.union.edu/~dpvc/jsmath/
|
||||
.. _preview-latex package: http://www.gnu.org/software/auctex/preview-latex.html
|
||||
.. _AmSMath LaTeX package: http://www.ams.org/tex/amslatex.html
|
@ -35,6 +35,7 @@ These extensions are built in and can be activated by respective entries in the
|
||||
ext/autodoc
|
||||
ext/doctest
|
||||
ext/intersphinx
|
||||
ext/math
|
||||
ext/refcounting
|
||||
ext/ifconfig
|
||||
ext/coverage
|
||||
|
@ -50,7 +50,7 @@ Modi:
|
||||
def main(argv=sys.argv):
|
||||
# delay-import these to be able to get sphinx.__version__ from setup.py
|
||||
# even without docutils installed
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.application import Sphinx, SphinxError
|
||||
from docutils.utils import SystemMessage
|
||||
|
||||
if not sys.stdout.isatty() or sys.platform == 'win32':
|
||||
@ -124,15 +124,7 @@ def main(argv=sys.argv):
|
||||
try:
|
||||
app = Sphinx(srcdir, confdir, outdir, doctreedir, buildername,
|
||||
confoverrides, status, sys.stderr, freshenv)
|
||||
if not app.builder:
|
||||
return 1
|
||||
|
||||
if all_files:
|
||||
app.builder.build_all()
|
||||
elif filenames:
|
||||
app.builder.build_specific(filenames)
|
||||
else:
|
||||
app.builder.build_update()
|
||||
app.build(all_files, filenames)
|
||||
except KeyboardInterrupt:
|
||||
if use_pdb:
|
||||
import pdb
|
||||
@ -151,6 +143,9 @@ def main(argv=sys.argv):
|
||||
if isinstance(err, SystemMessage):
|
||||
print >>sys.stderr, darkred('reST markup error:')
|
||||
print >>sys.stderr, err.args[0].encode('ascii', 'backslashreplace')
|
||||
elif isinstance(err, SphinxError):
|
||||
print >>sys.stderr, darkred('%s:' % err.category)
|
||||
print >>sys.stderr, err
|
||||
else:
|
||||
print >>sys.stderr, darkred('Exception occurred:')
|
||||
print >>sys.stderr, format_exception_cut_frames().rstrip()
|
||||
|
@ -13,6 +13,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
import posixpath
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives, roles
|
||||
@ -20,14 +21,22 @@ from docutils.parsers.rst import directives, roles
|
||||
import sphinx
|
||||
from sphinx.roles import xfileref_role, innernodetypes
|
||||
from sphinx.config import Config
|
||||
from sphinx.builder import builtin_builders
|
||||
from sphinx.builder import builtin_builders, StandaloneHTMLBuilder
|
||||
from sphinx.directives import desc_directive, target_directive, additional_xref_types
|
||||
from sphinx.environment import SphinxStandaloneReader
|
||||
from sphinx.util.console import bold
|
||||
|
||||
|
||||
class ExtensionError(Exception):
|
||||
class SphinxError(Exception):
|
||||
"""
|
||||
Base class for Sphinx errors that are shown to the user in a nicer
|
||||
way than normal exceptions.
|
||||
"""
|
||||
category = 'Sphinx error'
|
||||
|
||||
class ExtensionError(SphinxError):
|
||||
"""Raised if something's wrong with the configuration."""
|
||||
category = 'Extension error'
|
||||
|
||||
def __init__(self, message, orig_exc=None):
|
||||
self.message = message
|
||||
@ -53,6 +62,7 @@ events = {
|
||||
'doctree-resolved': 'doctree, docname',
|
||||
'env-updated': 'env',
|
||||
'html-page-context': 'pagename, context, doctree or None',
|
||||
'build-finished': 'exception',
|
||||
}
|
||||
|
||||
CONFIG_FILENAME = 'conf.py'
|
||||
@ -94,8 +104,7 @@ class Sphinx(object):
|
||||
print >>status, 'No builder selected, using default: html'
|
||||
buildername = 'html'
|
||||
if buildername not in self.builderclasses:
|
||||
print >>warning, 'Builder name %s not registered' % buildername
|
||||
return
|
||||
raise SphinxError('Builder name %s not registered' % buildername)
|
||||
|
||||
self.info(bold('Sphinx v%s, building %s' % (sphinx.__version__, buildername)))
|
||||
|
||||
@ -103,6 +112,20 @@ class Sphinx(object):
|
||||
self.builder = builderclass(self, freshenv=freshenv)
|
||||
self.emit('builder-inited')
|
||||
|
||||
def build(self, all_files, filenames):
|
||||
try:
|
||||
if all_files:
|
||||
self.builder.build_all()
|
||||
elif filenames:
|
||||
self.builder.build_specific(filenames)
|
||||
else:
|
||||
self.builder.build_update()
|
||||
except Exception, err:
|
||||
self.emit('build-finished', err)
|
||||
raise
|
||||
else:
|
||||
self.emit('build-finished', None)
|
||||
|
||||
def warn(self, message):
|
||||
self._warncount += 1
|
||||
self._warning.write('WARNING: %s\n' % message)
|
||||
@ -244,6 +267,10 @@ class Sphinx(object):
|
||||
def add_transform(self, transform):
|
||||
SphinxStandaloneReader.transforms.append(transform)
|
||||
|
||||
def add_javascript(self, filename):
|
||||
StandaloneHTMLBuilder.script_files.append(
|
||||
posixpath.join('_static', filename))
|
||||
|
||||
|
||||
class TemplateBridge(object):
|
||||
"""
|
||||
|
@ -305,6 +305,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
copysource = True
|
||||
out_suffix = '.html'
|
||||
indexer_format = json
|
||||
script_files = ['_static/jquery.js', '_static/doctools.js']
|
||||
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
||||
'image/jpeg']
|
||||
searchindex_filename = 'searchindex.json'
|
||||
@ -388,6 +389,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
shorttitle = self.config.html_short_title,
|
||||
show_sphinx = self.config.html_show_sphinx,
|
||||
file_suffix = self.out_suffix,
|
||||
script_files = self.script_files,
|
||||
sphinx_version = __version__,
|
||||
rellinks = rellinks,
|
||||
builder = self.name,
|
||||
|
52
sphinx/ext/jsmath.py
Normal file
52
sphinx/ext/jsmath.py
Normal file
@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.ext.jsmath
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Set up everything for use of JSMath to display math in HTML
|
||||
via JavaScript.
|
||||
|
||||
:copyright: 2008 by Georg Brandl.
|
||||
:license: BSD.
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.application import ExtensionError
|
||||
from sphinx.ext.mathbase import setup as mathbase_setup, wrap_displaymath
|
||||
|
||||
|
||||
def html_visit_math(self, node):
|
||||
self.body.append(self.starttag(node, 'span', '', CLASS='math'))
|
||||
self.body.append(self.encode(node['latex']) + '</span>')
|
||||
raise nodes.SkipNode
|
||||
|
||||
def html_visit_displaymath(self, node):
|
||||
for i, part in enumerate(node['latex'].split('\n\n')):
|
||||
part = self.encode(part)
|
||||
if i == 0:
|
||||
# necessary to e.g. set the id property correctly
|
||||
if node['number']:
|
||||
self.body.append('<span class="eqno">(%s)</span>' % node['number'])
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math'))
|
||||
else:
|
||||
# but only once!
|
||||
self.body.append('<div class="math">')
|
||||
if '&' in part or '\\\\' in part:
|
||||
self.body.append('\\begin{split}' + part + '\\end{split}')
|
||||
else:
|
||||
self.body.append(part)
|
||||
self.body.append('</div>\n')
|
||||
raise nodes.SkipNode
|
||||
|
||||
def builder_inited(app):
|
||||
if not app.config.jsmath_path:
|
||||
raise ExtensionError('jsmath_path config value must be set for the '
|
||||
'jsmath extension to work')
|
||||
app.add_javascript(app.config.jsmath_path)
|
||||
|
||||
|
||||
def setup(app):
|
||||
mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
|
||||
app.add_config_value('jsmath_path', '', False)
|
||||
app.connect('builder-inited', builder_inited)
|
135
sphinx/ext/mathbase.py
Normal file
135
sphinx/ext/mathbase.py
Normal file
@ -0,0 +1,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.ext.mathbase
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Set up math support in source files and LaTeX/text output.
|
||||
|
||||
:copyright: 2008 by Georg Brandl.
|
||||
:license: BSD.
|
||||
"""
|
||||
|
||||
from docutils import nodes, utils
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
|
||||
class math(nodes.Inline, nodes.TextElement):
|
||||
pass
|
||||
|
||||
class displaymath(nodes.Part, nodes.Element):
|
||||
pass
|
||||
|
||||
class eqref(nodes.Inline, nodes.TextElement):
|
||||
pass
|
||||
|
||||
|
||||
def wrap_displaymath(math, label):
|
||||
parts = math.split('\n\n')
|
||||
ret = []
|
||||
for i, part in enumerate(parts):
|
||||
if label is not None and i == 0:
|
||||
ret.append('\\begin{split}%s\\end{split}' % part +
|
||||
(label and '\\label{'+label+'}' or ''))
|
||||
else:
|
||||
ret.append('\\begin{split}%s\\end{split}\\notag' % part)
|
||||
return '\\begin{gather}\n' + '\\\\'.join(ret) + '\n\\end{gather}'
|
||||
|
||||
|
||||
def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
latex = utils.unescape(text, restore_backslashes=True)
|
||||
return [math(latex=latex)], []
|
||||
|
||||
def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
text = utils.unescape(text)
|
||||
node = eqref('(?)', '(?)', target=text)
|
||||
node['docname'] = inliner.document.settings.env.docname
|
||||
return [node], []
|
||||
|
||||
def math_directive(name, arguments, options, content, lineno,
|
||||
content_offset, block_text, state, state_machine):
|
||||
latex = '\n'.join(content)
|
||||
if arguments and arguments[0]:
|
||||
latex = arguments[0] + '\n\n' + latex
|
||||
node = displaymath()
|
||||
node['latex'] = latex
|
||||
node['label'] = options.get('label', None)
|
||||
node['docname'] = state.document.settings.env.docname
|
||||
ret = [node]
|
||||
if node['label']:
|
||||
tnode = nodes.target('', '', ids=['equation-' + node['label']])
|
||||
state.document.note_explicit_target(tnode)
|
||||
ret.insert(0, tnode)
|
||||
return ret
|
||||
|
||||
|
||||
def latex_visit_math(self, node):
|
||||
self.body.append('$' + node['latex'] + '$')
|
||||
raise nodes.SkipNode
|
||||
|
||||
def latex_visit_displaymath(self, node):
|
||||
self.body.append(wrap_displaymath(node['latex'],
|
||||
node['docname'] + '-' + node['label']))
|
||||
raise nodes.SkipNode
|
||||
|
||||
def latex_visit_eqref(self, node):
|
||||
self.body.append('\\eqref{%s-%s}' % (node['docname'], node['target']))
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def text_visit_math(self, node):
|
||||
self.add_text(node['latex'])
|
||||
raise nodes.SkipNode
|
||||
|
||||
def text_visit_displaymath(self, node):
|
||||
self.new_state()
|
||||
self.add_text(node['latex'])
|
||||
self.end_state()
|
||||
raise nodes.SkipNode
|
||||
|
||||
def text_visit_eqref(self, node):
|
||||
self.add_text(node['label'])
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def html_visit_eqref(self, node):
|
||||
self.body.append('<a href="#equation-%s">' % node['target'])
|
||||
|
||||
def html_depart_eqref(self, node):
|
||||
self.body.append('</a>')
|
||||
|
||||
|
||||
def number_equations(app, doctree, docname):
|
||||
num = 0
|
||||
numbers = {}
|
||||
for node in doctree.traverse(displaymath):
|
||||
if node['label'] is not None:
|
||||
num += 1
|
||||
node['number'] = num
|
||||
numbers[node['label']] = num
|
||||
else:
|
||||
node['number'] = None
|
||||
for node in doctree.traverse(eqref):
|
||||
if node['target'] not in numbers:
|
||||
continue
|
||||
num = '(%d)' % numbers[node['target']]
|
||||
node[0] = nodes.Text(num, num)
|
||||
|
||||
|
||||
def setup(app, htmlinlinevisitors, htmldisplayvisitors):
|
||||
app.add_node(math,
|
||||
latex=(latex_visit_math, None),
|
||||
text=(text_visit_math, None),
|
||||
html=htmlinlinevisitors)
|
||||
app.add_node(displaymath,
|
||||
latex=(latex_visit_displaymath, None),
|
||||
text=(text_visit_displaymath, None),
|
||||
html=htmldisplayvisitors)
|
||||
app.add_node(eqref,
|
||||
latex=(latex_visit_eqref, None),
|
||||
text=(text_visit_eqref, None),
|
||||
html=(html_visit_eqref, html_depart_eqref))
|
||||
app.add_role('math', math_role)
|
||||
app.add_role('eq', eq_role)
|
||||
app.add_directive('math', math_directive, 1, (0, 1, 1),
|
||||
label=directives.unchanged)
|
||||
app.connect('doctree-resolved', number_equations)
|
189
sphinx/ext/pngmath.py
Normal file
189
sphinx/ext/pngmath.py
Normal file
@ -0,0 +1,189 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.ext.pngmath
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Render math in HTML via dvipng.
|
||||
|
||||
:copyright: 2008 by Georg Brandl.
|
||||
:license: BSD.
|
||||
"""
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import posixpath
|
||||
from os import path
|
||||
from subprocess import Popen, PIPE
|
||||
try:
|
||||
from hashlib import sha
|
||||
except ImportError:
|
||||
from sha import sha
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.util import ensuredir
|
||||
from sphinx.application import SphinxError
|
||||
from sphinx.ext.mathbase import setup as mathbase_setup, wrap_displaymath
|
||||
|
||||
class MathExtError(SphinxError):
|
||||
category = 'Math extension error'
|
||||
|
||||
|
||||
DOC_HEAD = r'''
|
||||
\documentclass[12pt]{article}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{bm}
|
||||
\pagestyle{empty}
|
||||
'''
|
||||
|
||||
DOC_BODY = r'''
|
||||
\begin{document}
|
||||
%s
|
||||
\end{document}
|
||||
'''
|
||||
|
||||
DOC_BODY_PREVIEW = r'''
|
||||
\usepackage[active]{preview}
|
||||
\begin{document}
|
||||
\begin{preview}
|
||||
%s
|
||||
\end{preview}
|
||||
\end{document}
|
||||
'''
|
||||
|
||||
depth_re = re.compile(r'\[\d+ depth=(-?\d+)\]')
|
||||
|
||||
def render_math(self, math):
|
||||
"""
|
||||
Render the LaTeX math expression *math* using latex and dvipng.
|
||||
|
||||
Return the filename relative to the built document and the "depth",
|
||||
that is, the distance of image bottom and baseline in pixels, if the
|
||||
option to use preview_latex is switched on.
|
||||
|
||||
Error handling may seem strange, but follows a pattern: if LaTeX or
|
||||
dvipng aren't available, only a warning is generated (since that enables
|
||||
people on machines without these programs to at least build the rest
|
||||
of the docs successfully). If the programs are there, however, they
|
||||
may not fail since that indicates a problem in the math source.
|
||||
"""
|
||||
use_preview = self.builder.config.pngmath_use_preview
|
||||
|
||||
shasum = "%s.png" % sha(math).hexdigest()
|
||||
relfn = posixpath.join(self.builder.imgpath, 'math', shasum)
|
||||
outfn = path.join(self.builder.outdir, '_images', 'math', shasum)
|
||||
|
||||
latex = DOC_HEAD + self.builder.config.pngmath_latex_preamble
|
||||
latex += (use_preview and DOC_BODY_PREVIEW or DOC_BODY) % math
|
||||
if isinstance(latex, unicode):
|
||||
latex = latex.encode('utf-8')
|
||||
|
||||
# use only one tempdir per build -- the use of a directory is cleaner
|
||||
# than using temporary files, since we can clean up everything at once
|
||||
# just removing the whole directory (see cleanup_tempdir)
|
||||
if not hasattr(self.builder, '_mathpng_tempdir'):
|
||||
tempdir = self.builder._mathpng_tempdir = tempfile.mkdtemp()
|
||||
else:
|
||||
tempdir = self.builder._mathpng_tempdir
|
||||
|
||||
tf = open(path.join(tempdir, 'math.tex'), 'w')
|
||||
tf.write(latex)
|
||||
tf.close()
|
||||
|
||||
ltx_args = [self.builder.config.pngmath_latex,
|
||||
'--interaction=nonstopmode',
|
||||
'--output-directory=' + tempdir,
|
||||
'math.tex']
|
||||
try:
|
||||
p = Popen(ltx_args, stdout=PIPE, stderr=PIPE)
|
||||
except OSError, err:
|
||||
if err.errno != 2: # No such file or directory
|
||||
raise
|
||||
if not hasattr(self.builder, '_mathpng_warned_latex'):
|
||||
self.builder.warn('LaTeX command %r cannot be run (needed for math '
|
||||
'display), check the pngmath_latex setting' %
|
||||
self.builder.config.pngmath_latex)
|
||||
self.builder._mathpng_warned_latex = True
|
||||
return relfn, None
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise MathExtError('latex exited with error:\n' + stdout)
|
||||
|
||||
ensuredir(path.dirname(outfn))
|
||||
# use some standard dvipng arguments
|
||||
dvipng_args = [self.builder.config.pngmath_dvipng, '-o', outfn,
|
||||
'-bg', 'Transparent', '-T', 'tight', '-z9']
|
||||
# add custom ones from config value
|
||||
dvipng_args.extend(self.builder.config.pngmath_dvipng_args)
|
||||
if use_preview:
|
||||
dvipng_args.append('--depth')
|
||||
# last, the input file name
|
||||
dvipng_args.append(path.join(tempdir, 'math.dvi'))
|
||||
try:
|
||||
p = Popen(dvipng_args, stdout=PIPE, stderr=PIPE)
|
||||
except OSError, err:
|
||||
if err.errno != 2: # No such file or directory
|
||||
raise
|
||||
if not hasattr(self.builder, '_mathpng_warned_dvipng'):
|
||||
self.builder.warn('dvipng command %r cannot be run (needed for math '
|
||||
'display), check the pngmath_dvipng setting' %
|
||||
self.builder.config.pngmath_dvipng)
|
||||
self.builder._mathpng_warned_dvipng = True
|
||||
return relfn, None
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise MathExtError('dvipng exited with error:\n[stdout]\n%s\n[stderr]%s'
|
||||
% (stdout, stderr))
|
||||
depth = None
|
||||
if use_preview:
|
||||
for line in stdout.splitlines():
|
||||
m = depth_re.match(line)
|
||||
if m:
|
||||
depth = int(m.group(1))
|
||||
break
|
||||
|
||||
return relfn, depth
|
||||
|
||||
def cleanup_tempdir(app, exc):
|
||||
if exc:
|
||||
return
|
||||
if not hasattr(app.builder, '_mathpng_tempdir'):
|
||||
return
|
||||
try:
|
||||
shutil.rmtree(app.builder._mathpng_tempdir)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def html_visit_math(self, node):
|
||||
fname, depth = render_math(self, '$'+node['latex']+'$')
|
||||
self.body.append('<img src="%s" alt="%s" %s/>' %
|
||||
(fname, self.encode(node['latex']),
|
||||
depth and 'style="vertical-align: %dpx" ' % (-depth) or ''))
|
||||
raise nodes.SkipNode
|
||||
|
||||
def html_visit_displaymath(self, node):
|
||||
latex = wrap_displaymath(node['latex'], None)
|
||||
fname, depth = render_math(self, latex)
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math'))
|
||||
self.body.append('<p>')
|
||||
if node['number']:
|
||||
self.body.append('<span class="eqno">(%s)</span>' % node['number'])
|
||||
self.body.append('<img src="%s" alt="%s" />\n</div>' %
|
||||
(fname, self.encode(node['latex'])))
|
||||
self.body.append('</p>')
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def setup(app):
|
||||
mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
|
||||
app.add_config_value('pngmath_dvipng', 'dvipng', False)
|
||||
app.add_config_value('pngmath_latex', 'latex', False)
|
||||
app.add_config_value('pngmath_use_preview', False, False)
|
||||
app.add_config_value('pngmath_dvipng_args', [], False)
|
||||
app.add_config_value('pngmath_latex_preamble', '', False)
|
||||
app.connect('build-finished', cleanup_tempdir)
|
@ -814,6 +814,14 @@ form.comment textarea {
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
div.math {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* :::: PRINT :::: */
|
||||
@media print {
|
||||
div.document,
|
||||
|
File diff suppressed because one or more lines are too long
@ -502,3 +502,11 @@ ul.search li div.context {
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.math {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
@ -108,9 +108,9 @@
|
||||
FILE_SUFFIX: '{{ file_suffix }}'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="{{ pathto('_static/jquery.js', 1) }}"></script>
|
||||
<script type="text/javascript" src="{{ pathto('_static/interface.js', 1) }}"></script>
|
||||
<script type="text/javascript" src="{{ pathto('_static/doctools.js', 1) }}"></script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
{%- if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml"
|
||||
title="Search within {{ docstitle }}"
|
||||
|
@ -1,8 +1,6 @@
|
||||
{% extends "layout.html" %}
|
||||
{% set title = 'Search' %}
|
||||
{% block extrahead %}
|
||||
<script type="text/javascript" src="{{ pathto('_static/searchtools.js', 1) }}"></script>
|
||||
{% endblock %}
|
||||
{% set script_files = script_files + ['searchtools.js'] %}
|
||||
{% block body %}
|
||||
<h1 id="search-documentation">Search</h1>
|
||||
<p>
|
||||
|
Loading…
Reference in New Issue
Block a user