From 706e12da65b1ae240d66c24f910d7f081d700c26 Mon Sep 17 00:00:00 2001 From: Roland Meister Date: Mon, 28 Dec 2009 17:09:09 +0100 Subject: [PATCH] Initial import from local repository --- doc/Makefile | 6 + doc/builders.rst | 12 + doc/conf.py | 12 + doc/config.rst | 93 ++++++ doc/faq.rst | 41 +++ doc/theming.rst | 4 + sphinx/builders/__init__.py | 1 + sphinx/builders/epub.py | 409 ++++++++++++++++++++++++++ sphinx/config.py | 14 + sphinx/quickstart.py | 68 ++++- sphinx/themes/epub/layout.html | 7 + sphinx/themes/epub/static/epub.css | 445 +++++++++++++++++++++++++++++ sphinx/themes/epub/theme.conf | 4 + tests/test_build.py | 4 + tests/test_theming.py | 2 +- 15 files changed, 1119 insertions(+), 3 deletions(-) create mode 100644 sphinx/builders/epub.py create mode 100644 sphinx/themes/epub/layout.html create mode 100644 sphinx/themes/epub/static/epub.css create mode 100644 sphinx/themes/epub/theme.conf diff --git a/doc/Makefile b/doc/Makefile index 07cee7449..ffd51dc64 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -19,6 +19,7 @@ help: @echo " dirhtml to make HTML files called index.html in directories" @echo " pickle to make pickle files" @echo " htmlhelp to make HTML files and a HTML help project" + @echo " epub to make an epub file" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @@ -65,6 +66,11 @@ qthelp: @echo "To view the help collection:" @echo "# assistant -collectionFile _build/qthelp/Sphinx.qhc" +epub: + mkdir -p _build/epub _build/doctrees + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub + @echo + @echo "Build finished. The epub file is in _build/epub." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex diff --git a/doc/builders.rst b/doc/builders.rst index 7c1f03d3d..3ce8f56a7 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -63,6 +63,18 @@ The builder's "name" must be given to the **-b** command-line option of Its name is ``devhelp``. +.. module:: sphinx.builders.epub +.. class:: EpubBuilder + + This builder produces the same output as the standalone HTML builder, but + also generates an *epub* file for ebook readers. + This builder is meant to be used together with the + :confval:`html_theme` ``'epub'``. + See ``_ or + ``_ for the definition of epubs. + + Its name is ``epub``. + .. module:: sphinx.builders.latex .. class:: LaTeXBuilder diff --git a/doc/conf.py b/doc/conf.py index e3952a0b2..f8310746c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -64,6 +64,18 @@ html_use_opensearch = 'http://sphinx.pocoo.org' # Output file base name for HTML help builder. htmlhelp_basename = 'Sphinxdoc' +# Epub fields +epub_basename = 'sphinx' +epub_author = 'Georg Brandl' +epub_publisher = 'http://sphinx.pocoo.org/' +epub_scheme = 'url' +epub_identifier = epub_publisher +epub_pre_files = [ ('index', 'Welcome')] +epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js', + '_static/jquery.js', '_static/searchtools.js', + '_static/basic.css', 'search.html'] + + # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation', diff --git a/doc/config.rst b/doc/config.rst index 3ce8d53bb..99b484c85 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -549,6 +549,99 @@ that use Sphinx' HTMLWriter class. Output file base name for HTML help builder. Default is ``'pydoc'``. +.. _epub-options: + +Options for epub output +----------------------- + +These options influence the epub output. As this writer derives from the +HTMLWriter the HTML options also apply where appropriate. +The actual values for some of the options is not really important, +they just have to be entered into +the `Dublin Core metadata `_. + +.. confval:: epub_basename + + The basename for the epub file. It defaults to the :confval:`project` name. + +.. confval:: epub_title + + The title of the document. + It defaults to the :confval:`html_title` option + but can be set independently for epub creation. + +.. confval:: epub_author + + The author of the document. + This is put in the Dublin Core metadata. + The default value is ``'unknown'``. + +.. confval:: epub_language + + The language of the document. + This is put in the Dublin Core metadata. + The default is the :confval:`language` option or ``'en'`` if unset. + +.. confval:: epub_publisher + + The publisher of the document. + This is put in the Dublin Core metadata. + You may use any sensible string, e.g. the project homepage. + The default value is ``'unknown'``. + +.. confval:: epub_copyright + + The copyright of the document. + It defaults to the :confval:`copyright` option + but can be set independently for epub creation. + +.. confval:: epub_identifier + + An identifier for the document. + This is put in the Dublin Core metadata. + For published documents this is the ISBN number, but you can + also use an alternative scheme, e.g. the project homepage. + The default value is ``'unknown'``. + +.. confval:: epub_scheme + + The publication scheme for the :confval:`epub_identifier`. + This is put in the Dublin Core metadata. + For published books the scheme is ``'ISBN'``. + If you use the project homepage, ``'URL'`` seems reasonable. + +.. confval:: epub_uid + + A unique identifier for the document. + This is put in the Dublin Core metadata. + You may use a random string. + The default value is ``'unknown'``. + +.. confval:: epub_pre_files + + Additional files that should be inserted before the text generated by + Sphinx. It is a list of tuples containing the file name and the title. + Example:: + + epub_pre_files = [ + ('index.html', 'Welcome'), + ] + + The default value is ``[]``. + +.. confval:: epub_post_files + + Additional files that should be inserted after the text generated by + Sphinx. It is a list of tuples containing the file name and the title. + The default value is ``[]``. + +.. confval:: epub_exclude_files + + A list of files that are generated/copied in the build directory + but should not be included in the epub file. + The default value is ``[]``. + + .. _latex-options: Options for LaTeX output diff --git a/doc/faq.rst b/doc/faq.rst index 34502dc41..46a714dd0 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -54,5 +54,46 @@ github pages Sphinx HTML output. +Epub +---- + +The EpubBuilder is currently in an experimental stage. +It has only been tested with the Sphinx documentation itself. +If you want to create epubs, here are some notes: + +* Split the text into several files. The longer the individual HTML files + are, the longer it takes the ebook reader to render them. + In extreme cases, the rendering can take up to one minute. + +* Try to minimize the markup. This also pays in rendering time. + +* For some readers you can use embedded or external fonts + using the CSS ``@font-face`` directive. + This is *extremely* useful for code listings which are often cut + at the right margin. The default Courier font (or variant) is quite + wide and you can only display up to 60 characters on a line. + If you replace it with a narrower font, you can get more characters + on a line. You may even use + `fontforge `_ + and create narrow variants + of some free font. In my case I get up to 70 characters on a line. + + You may have to experiment a little until you get reasonable results. + +* Test the created epubs. You can use several alternatives. + The ones I am aware of are + Epubcheck + (`http://code.google.com/p/epubcheck/ + `_), + Calibre + (`http://calibre-ebook.com/ `_), + FBreader (`http://www.fbreader.org/ `_, + although it does not render the CSS), and + Bookworm (`http://bookworm.oreilly.com/ `_). + For bookworm you can download the source from + `http://code.google.com/p/threepress/ `_ + and run you own local server. + + .. _api role: http://git.savannah.gnu.org/cgit/kenozooid.git/tree/doc/extapi.py .. _xhtml to reST: http://docutils.sourceforge.net/sandbox/xhtml2rest/xhtml2rest.py diff --git a/doc/theming.rst b/doc/theming.rst index 7af54a0be..4f7cadd1d 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -160,6 +160,10 @@ These themes are: * **traditional** -- A theme resembling the old Python documentation. There are currently no options beyond *nosidebar*. +* **epub** -- A theme for the epub formatter. There are currently no + options. This theme tries to reduce visual space which is a sparse + resource on ebook readers. + Creating themes --------------- diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index a8fc8871c..b35d0fd16 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -388,6 +388,7 @@ BUILTIN_BUILDERS = { 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'), 'devhelp': ('devhelp', 'DevhelpBuilder'), 'qthelp': ('qthelp', 'QtHelpBuilder'), + 'epub': ('epub', 'EpubBuilder'), 'latex': ('latex', 'LaTeXBuilder'), 'text': ('text', 'TextBuilder'), 'changes': ('changes', 'ChangesBuilder'), diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py new file mode 100644 index 000000000..57e2dff9a --- /dev/null +++ b/sphinx/builders/epub.py @@ -0,0 +1,409 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.epub + ~~~~~~~~~~~~~~~~~~~~ + + Build epub files. + Originally derived from qthelp.py. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import codecs +from os import path +import zipfile + +from docutils import nodes + +from sphinx import addnodes +from sphinx.builders.html import StandaloneHTMLBuilder + + +# (Fragment) templates from which the metainfo files +# content.opf, toc.ncx, mimetype, and META-INF/container.xml +# are created. + +_mimetype_template = 'application/epub+zip' # no EOL! + +_container_template = u'''\ + + + + + + +''' + +_toc_template = u'''\ + + + + + + + + + + %(title)s + + +%(navpoints)s + + +''' + +_navpoint_template = u'''\ +%(indent)s +%(indent)s +%(indent)s %(text)s +%(indent)s +%(indent)s +%(indent)s ''' + +_navpoint_indent = ' ' +_navPoint_template = 'navPoint%d' + +_content_template = u'''\ + + + + %(lang)s + %(title)s + %(author)s + %(publisher)s + %(copyright)s + %(id)s + + + +%(files)s + + +%(spine)s + + +''' + +_file_template = u'''\ + ''' + +_spine_template = u'''\ + ''' + +_toctree_template = u'toctree-l%d' + +_media_types = { + '.html': 'application/xhtml+xml', + '.css': 'text/css', + '.png': 'image/png', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.otf': 'application/x-font-otf', + '.ttf': 'application/x-font-ttf', +} + + +# The epub publisher + +class EpubBuilder(StandaloneHTMLBuilder): + """Builder that outputs epub files. + It creates the metainfo files + container.opf, toc.ncx, mimetype, and META-INF/container.xml. + Afterwards, all necessary files are zipped to an epub file. + """ + name = 'epub' + + # don't copy the reST source + copysource = False + supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', + 'image/jpeg'] + + # don't add links + add_permalinks = False + # don't add sidebar etc. + embedded = True + + def init(self): + StandaloneHTMLBuilder.init(self) + # the output files for HTML help must be .html only + self.out_suffix = '.html' + self.playorder = 0 + + + # generic support functions + def make_id(self, name): + """Replace all characters not allowed for (X)HTML ids""" + return name.replace('/', '_') + + def esc(self, name): + """Replace all characters not allowed in text an attribute values""" + # Like cgi.escape, but also replace apostrophe + name = name.replace('&', '&') + name = name.replace('<', '<') + name = name.replace('>', '>') + name = name.replace('"', '"') + name = name.replace('\'', ''') + return name + + def collapse_text(self, doctree, result): + """Remove all HTML markup and return only the text nodes""" + for c in doctree.children: + if isinstance(c, nodes.Text): + result.append(c.data) + else: + result = self.collapse_text(c, result) + return result + + def get_refnodes(self, doctree, result): + """Collect section titles, their depth in the toc and the refuri""" + # XXX: is there a betterr way than checking the attribute + # toctree-l[1-6] on the parent node? + if isinstance(doctree, nodes.reference): + classes = doctree.parent.attributes['classes'] + level = 1 + for l in range(5,0,-1): # or range(1,6)? + if (_toctree_template % l) in classes: + level = l + result.append({ + 'level': level, + 'refuri': self.esc(doctree['refuri']), + 'text': self.esc(''.join(self.collapse_text(doctree, []))) + }) + else: + for elem in doctree.children: + result = self.get_refnodes(elem, result) + return result + + def get_toc(self): + """Get the total table of contents, containg the master_doc + and pre and post files not managed by sphinx""" + doctree = self.env.get_and_resolve_doctree(self.config.master_doc, self) + self.refnodes = self.get_refnodes(doctree, []) + self.refnodes.insert(0, { + 'level': 1, + 'refuri': self.esc(self.config.master_doc + '.html'), + 'text': self.esc(''.join(self.collapse_text( + self.env.titles[self.config.master_doc], [] + ))), + }) + # XXX: is reversed ok? + for file, text in reversed(self.config.epub_pre_files): + self.refnodes.insert(0, { + 'level': 1, + 'refuri': self.esc(file + '.html'), + 'text': self.esc(text) + }) + for file, text in self.config.epub_post_files: + self.refnodes.append({ + 'level': 1, + 'refuri': self.esc(file + '.html'), + 'text': self.esc(text) + }) + + + # Finish by building the epub file + def handle_finish(self): + """Create the metainfo files and finally the epub""" + self.get_toc() + self.build_mimetype(self.outdir, 'mimetype') + self.build_container(self.outdir, 'META-INF/container.xml') + self.build_content(self.outdir, 'content.opf') + self.build_toc(self.outdir, 'toc.ncx') + self.build_epub(self.outdir, self.config.epub_basename + '.epub') + + def build_mimetype(self, outdir, outname): + """Write the metainfo file mimetype""" + self.info('writing %s file...' % outname) + f = codecs.open(path.join(outdir, outname), 'w', 'utf-8') + try: + f.write(_mimetype_template) + finally: + f.close() + + def build_container(self, outdir, outname): + """Write the metainfo file META-INF/cointainer.xml""" + self.info('writing %s file...' % outname) + fn = path.join(outdir, outname) + try: + os.mkdir(path.dirname(fn)) + except OSError, err: + if err.errno != os.errno.EEXIST: + raise + f = codecs.open(path.join(outdir, outname), 'w', 'utf-8') + try: + f.write(_container_template) + finally: + f.close() + + def content_metadata(self, files, spine): + """Create a dictionary with all metadata for the content.opf + file properly escaped""" + metadata = {} + metadata['title'] = self.esc(self.config.epub_title) + metadata['author'] = self.esc(self.config.epub_author) + metadata['uid'] = self.esc(self.config.epub_uid) + metadata['lang'] = self.esc(self.config.epub_language) + metadata['publisher'] = self.esc(self.config.epub_publisher) + metadata['copyright'] = self.esc(self.config.epub_copyright) + metadata['scheme'] = self.esc(self.config.epub_scheme) + metadata['id'] = self.esc(self.config.epub_identifier) + metadata['files'] = files + metadata['spine'] = spine + return metadata + + def build_content(self, outdir, outname): + """Write the metainfo file content.opf + It contains bibliographic data, a file list and + the spine (the reading order).""" + self.info('writing %s file...' % outname) + + # files + if not outdir.endswith(os.sep): + outdir += os.sep + olen = len(outdir) + projectfiles = [] + self.files = [] + self.ignored_files = ['.buildinfo', + 'mimetype', 'content.opf', 'toc.ncx', 'META-INF/container.xml', + self.config.epub_basename + '.epub'] + \ + self.config.epub_exclude_files + for root, dirs, files in os.walk(outdir): + for fn in files: + filename = path.join(root, fn)[olen:] + if filename in self.ignored_files: + # self.warn("ignoring %s" % filename) + continue + ext = path.splitext(filename)[-1] + if ext not in _media_types: + self.warn("unknown mimetype for %s, ignoring" % filename) + continue + projectfiles.append(_file_template % { + 'href': self.esc(filename), + 'id': self.esc(self.make_id(filename)), + 'media_type': self.esc(_media_types[ext]) + }) + self.files.append(filename) + projectfiles = '\n'.join(projectfiles) + + # spine + spine = [] + for item in self.refnodes: + if '#' in item['refuri']: + continue + if item['refuri'] in self.ignored_files: + continue + spine.append(_spine_template % { + 'idref': self.esc(self.make_id(item['refuri'])) + }) + spine = '\n'.join(spine) + + # write the project file + f = codecs.open(path.join(outdir, outname), 'w', 'utf-8') + try: + f.write(_content_template % \ + self.content_metadata(projectfiles, spine)) + finally: + f.close() + + def new_navpoint(self, node, level, incr=True): + """Create a new entry in the toc from the node at given level""" + # XXX Modifies the node + if incr: + self.playorder += 1 + node['indent'] = _navpoint_indent * level + node['navpoint'] = self.esc(_navPoint_template % self.playorder) + node['playorder'] = self.playorder + return _navpoint_template % node + + def insert_subnav(self, node, subnav): + """Insert nested navpoints for given node + The node and subnav are already rendered to text""" + nlist = node.split('\n') + nlist.insert(-1, subnav) + return '\n'.join(nlist) + + def build_navpoints(self, nodes): + """Create the toc navigation structure + Subelements of a node are nested inside the navpoint. + For nested nodes the parent node is reinserted in the subnav.""" + navstack = [] + navlist = [] + level = 1 + lastnode = None + for node in nodes: + file = node['refuri'].split('#')[0] + if file in self.ignored_files: + continue + if node['level'] == level: + navlist.append(self.new_navpoint(node,level)) + elif node['level'] == level + 1: + navstack.append(navlist) + navlist = [] + level += 1 + if lastnode: + # Insert starting point in subtoc with same playOrder + navlist.append(self.new_navpoint(lastnode, level, False)) + navlist.append(self.new_navpoint(node, level)) + else: + while node['level'] < level: + subnav = '\n'.join(navlist) + navlist = navstack.pop() + navlist[-1] = self.insert_subnav(navlist[-1], subnav) + level -= 1 + navlist.append(self.new_navpoint(node, level)) + lastnode = node + while level != 1: + subnav = '\n'.join(navlist) + navlist = navstack.pop() + navlist[-1] = self.insert_subnav(navlist[-1], subnav) + level -= 1 + return '\n'.join(navlist) + + def toc_metadata(self, level, navpoints): + """Create a dictionary with all metadata for the toc.ncx + file properly escaped""" + metadata = {} + metadata['uid'] = self.config.epub_uid + metadata['title'] = self.config.epub_title + metadata['level'] = level + metadata['navpoints'] = navpoints + return metadata + + def build_toc(self, outdir, outname): + """Write the metainfo file toc.ncx""" + self.info('writing %s file...' % outname) + + navpoints = self.build_navpoints(self.refnodes) + level = max(item['level'] for item in self.refnodes) + f = codecs.open(path.join(outdir, outname), 'w', 'utf-8') + try: + f.write(_toc_template % self.toc_metadata(level, navpoints)) + finally: + f.close() + + def build_epub(self, outdir, outname): + """Write the epub file + It is a zip file with the mimetype file stored uncompressed + as the first entry.""" + self.info('writing %s file...' % outname) + projectfiles = ['META-INF/container.xml', 'content.opf', 'toc.ncx'] \ + + self.files + epub = zipfile.ZipFile(path.join(outdir, outname), 'w', \ + zipfile.ZIP_DEFLATED) + epub.write(path.join(outdir, 'mimetype'), 'mimetype', \ + zipfile.ZIP_STORED) + for file in projectfiles: + epub.write(path.join(outdir, file), file, zipfile.ZIP_DEFLATED) + epub.close() + diff --git a/sphinx/config.py b/sphinx/config.py index b2ef29fb1..5050a7796 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -112,6 +112,20 @@ class Config(object): latex_docclass = ({}, None), # now deprecated - use latex_elements latex_preamble = ('', None), + + # Epub options + epub_basename = (lambda self: make_filename(self.project), None), + epub_title = (lambda self: self.html_title, 'html'), + epub_author = ('unknown', 'html'), + epub_language = (lambda self: self.language or 'en', 'html'), + epub_publisher = ('unknown', 'html'), + epub_copyright = (lambda self: self.copyright, 'html'), + epub_identifier = ('unknown', 'html'), + epub_scheme = ('unknown', 'html'), + epub_uid = ('unknown', 'env'), + epub_pre_files = ([], 'env'), + epub_post_files = ([], 'env'), + epub_exclude_files = ([], 'env'), ) def __init__(self, dirname, filename, overrides, tags): diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 7b11dfdd4..eaf9f55a0 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -223,6 +223,56 @@ latex_documents = [ # If false, no module index is generated. #latex_use_modindex = True + + +# -- Options for Epub output --------------------------------------------------- + +# Please also set the html_theme to 'epub' or any other approriate theme. +# The display size is quite small for ebook readers. +# The default themes may take too much space. + +# bibliographic Dublin Core description of the content.opf and +# in the toc.ncx file. It defaults to the html_title option. +#epub_title = '' + +# The author of the text. The author is inserted into the +# bibliographic Dublin Core description of the content.opf file. +#epub_author = '' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The publisher of the text. The publisher is inserted into the +# bibliographic Dublin Core description of the content.opf file. +# You may use the project homepage. +#epub_publisher = '' + +# The copyright of the text. The copyright is inserted into the +# bibliographci Dublin Core description of the content.opf file. +# It defaults to the copyright option. +#epub_copyright = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# A unique identification for the text. +#epub_uid = '' + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +#epub_exclude_files = [] ''' INTERSPHINX_CONFIG = ''' @@ -270,8 +320,8 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) %(rsrcdir)s -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes \ -linkcheck doctest +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp epub \ +latex changes linkcheck doctest help: \t@echo "Please use \\`make ' where is one of" @@ -282,6 +332,7 @@ help: \t@echo " htmlhelp to make HTML files and a HTML help project" \t@echo " qthelp to make HTML files and a qthelp project" \t@echo " devhelp to make HTML files and a Devhelp project" +\t@echo " epub to make an epub" \t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" \t@echo " latexpdf to make LaTeX files and run them through pdflatex" \t@echo " changes to make an overview of all changed/added/deprecated items" @@ -337,6 +388,11 @@ devhelp: $$HOME/.local/share/devhelp/%(project_fn)s" \t@echo "# devhelp" +epub: +\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub +\t@echo +\t@echo "Build finished. The epub file is in $(BUILDDIR)/epub." + latex: \t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex \t@echo @@ -391,6 +447,7 @@ if "%%1" == "help" ( \techo. htmlhelp to make HTML files and a HTML help project \techo. qthelp to make HTML files and a qthelp project \techo. devhelp to make HTML files and a Devhelp project +\techo. epub to make an epub \techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter \techo. changes to make an overview over all changed/added/deprecated items \techo. linkcheck to check all external links for integrity @@ -458,6 +515,13 @@ if "%%1" == "devhelp" ( \tgoto end ) +if "%%1" == "epub" ( +\t%%SPHINXBUILD%% -b epub %%ALLSPHINXOPTS%% %%BUILDDIR%%/epub +\techo. +\techo.Build finished. The epub file is in %%BUILDDIR%%/epub. +\tgoto end +) + if "%%1" == "latex" ( \t%%SPHINXBUILD%% -b latex %%ALLSPHINXOPTS%% %%BUILDDIR%%/latex \techo. diff --git a/sphinx/themes/epub/layout.html b/sphinx/themes/epub/layout.html new file mode 100644 index 000000000..64b1a4cb2 --- /dev/null +++ b/sphinx/themes/epub/layout.html @@ -0,0 +1,7 @@ +{% extends "basic/layout.html" %} + +{# add only basic navigation links #} +{% block sidebar1 %}{% endblock %} +{% block sidebar2 %}{% endblock %} +{% block relbar2 %}{% endblock %} +{% block linktags %}{% endblock %} diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css new file mode 100644 index 000000000..72b771905 --- /dev/null +++ b/sphinx/themes/epub/static/epub.css @@ -0,0 +1,445 @@ +/** + * Sphinx stylesheet -- epub theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +a:link, a:visited { + color: #3333ff; + text-decoration: underline; +} + +img { + border: 0; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-family: sans-serif; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 100%; +} + +img { + border: 0; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 130%; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +/* -- general body styles --------------------------------------------------- */ + +a.headerlink { + visibility: hidden; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 100%; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 110%; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +/* -- other body styles ----------------------------------------------------- */ + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlight { + background-color: #ddd; +} + +dl.glossary dt { + font-weight: bold; + font-size: 110%; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.refcount { + color: #060; +} + +.optional { + font-size: 130%; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #dddddd; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + font-family: "LiberationNarrow", monospace; + overflow: auto; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt { + font-family: "LiberationNarrow", monospace; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- special divs --------------------------------------------------------- */ + +div.quotebar { + background-color: #e3eff1; + max-width: 250px; + float: right; + font-family: sans-serif; + padding: 7px 7px; + border: 1px solid #ccc; +} +div.footer { + background-color: #e3eff1; + padding: 3px 8px 3px 0; + clear: both; + font-family: sans-serif; + font-size: 80%; + text-align: right; +} + +div.footer a { + text-decoration: underline; +} + +@font-face { + font-family: "LiberationNarrow"; + font-style: normal; + font-weight: normal; + src: url("res:///Data/fonts/LiberationNarrow-Regular.otf") + format("opentype"); +} +@font-face { + font-family: "LiberationNarrow"; + font-style: oblique, italic; + font-weight: normal; + src: url("res:///Data/fonts/LiberationNarrow-Italic.otf") + format("opentype"); +} +@font-face { + font-family: "LiberationNarrow"; + font-style: normal; + font-weight: bold; + src: url("res:///Data/fonts/LiberationNarrow-Bold.otf") + format("opentype"); +} +@font-face { + font-family: "LiberationNarrow"; + font-style: oblique, italic; + font-weight: bold; + src: url("res:///Data/fonts/LiberationNarrow-BoldItalic.otf") + format("opentype"); +} + diff --git a/sphinx/themes/epub/theme.conf b/sphinx/themes/epub/theme.conf new file mode 100644 index 000000000..d5806ec50 --- /dev/null +++ b/sphinx/themes/epub/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = epub.css +pygments_style = none diff --git a/tests/test_build.py b/tests/test_build.py index 6668e2453..6d0cb9334 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -277,6 +277,10 @@ def test_htmlhelp(app): def test_qthelp(app): app.builder.build_all() +@with_app(buildername='epub') +def test_epub(app): + app.builder.build_all() + @with_app(buildername='changes', cleanenv=True) def test_changes(app): app.builder.build_all() diff --git a/tests/test_theming.py b/tests/test_theming.py index 065468b07..cdaf2baff 100644 --- a/tests/test_theming.py +++ b/tests/test_theming.py @@ -25,7 +25,7 @@ def test_theme_api(app): # test Theme class API assert set(Theme.themes.keys()) == \ set(['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc', - 'traditional', 'testtheme', 'ziptheme']) + 'traditional', 'testtheme', 'ziptheme', 'epub']) assert Theme.themes['testtheme'][1] is None assert isinstance(Theme.themes['ziptheme'][1], zipfile.ZipFile)