Add image format handling.

This commit is contained in:
Georg Brandl 2008-06-15 14:31:16 +00:00
parent 66b5f39bc7
commit b05861454c
7 changed files with 95 additions and 32 deletions

View File

@ -38,6 +38,9 @@ New features added
- The directories in the `html_static_path` can now contain
subdirectories.
* The image directive now supports specifying the extension as ``.*``,
which makes the builder select the one that matches best.
* The new config value `exclude_trees` can be used to exclude whole
subtrees from the search for source files.

9
TODO
View File

@ -13,12 +13,3 @@ Sphinx
- "often used" combo box in sidebar
- source file cross-references?
Web App
*******
- fix /download
- discuss and debug comments system
- prepare for databases other than sqlite for comments
- add search via Xapian or Nucular (Python indexer - nucular.sf.net)
- optionally have a contents tree view in the sidebar (AJAX based)?

View File

@ -214,12 +214,27 @@ Images
reST supports an image directive, used like so::
.. image:: filename
.. image:: gnu.png
(options)
When used within Sphinx, the ``filename`` given must be relative to the source
file, and Sphinx will automatically copy image files over to a subdirectory of
the output directory on building.
When used within Sphinx, the file name given (here ``gnu.png``) must be relative
to the source file, and Sphinx will automatically copy image files over to a
subdirectory of the output directory on building (e.g. the ``_static`` directory
for HTML output.)
Sphinx extends the standard docutils behavior by allowing an asterisk for the
extension::
.. image:: gnu.*
Sphinx then searches for all images matching the provided pattern and determines
their type. Each builder then chooses the best image out of these candidates.
For instance, if the file name ``gnu.*`` was given and two files :file:`gnu.pdf`
and :file:`gnu.png` existed in the source tree, the LaTeX builder would choose
the former, while the HTML builder would prefer the latter.
.. versionchanged:: 0.4
Added the support for file names ending in an asterisk.
Footnotes

View File

@ -62,6 +62,9 @@ class Builder(object):
self.info = app.info
self.config = app.config
# images that need to be copied over (source -> dest)
self.images = {}
# if None, this is set in load_env()
self.env = env
self.freshenv = freshenv
@ -118,6 +121,28 @@ class Builder(object):
if l == 0:
self.info()
supported_image_types = []
def post_process_images(self, doctree):
"""
Pick the best candidate for all image URIs.
"""
for node in doctree.traverse(nodes.image):
uri = node['candidates'].get('*', None)
if not uri:
for imgtype in self.supported_image_types:
uri = node['candidates'].get(imgtype, None)
if uri:
node['uri'] = uri
break
else:
self.warn('%s:%s: %s' %
(node.source, node.lineno,
'No matching candidate for uri: %(uri)s' % node))
continue
if uri in self.env.images:
self.images[uri] = self.env.images[uri][1]
# build methods
def load_env(self):
@ -271,6 +296,8 @@ class StandaloneHTMLBuilder(Builder):
copysource = True
out_suffix = '.html'
indexer_format = 'json'
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
'image/jpeg']
def init(self):
"""Load templates."""
@ -323,7 +350,7 @@ class StandaloneHTMLBuilder(Builder):
favicon = self.config.html_favicon and \
path.basename(self.config.html_favicon) or ''
if os.path.splitext(favicon)[1] != '.ico':
if favicon and os.path.splitext(favicon)[1] != '.ico':
self.warn('html_favicon is not an .ico file')
if not isinstance(self.config.html_use_opensearch, basestring):
@ -407,6 +434,7 @@ class StandaloneHTMLBuilder(Builder):
)
def write_doc(self, docname, doctree):
self.post_process_images(doctree)
destination = StringOutput(encoding='utf-8')
doctree.settings = self.docsettings
@ -504,10 +532,10 @@ class StandaloneHTMLBuilder(Builder):
self.info()
# copy image files
if self.env.images:
if self.images:
self.info(bold('copying images...'), nonl=1)
ensuredir(path.join(self.outdir, '_images'))
for src, (_, dest) in self.env.images.iteritems():
for src, dest in self.images.iteritems():
self.info(' '+src, nonl=1)
shutil.copyfile(path.join(self.srcdir, src),
path.join(self.outdir, '_images', dest))
@ -636,6 +664,8 @@ class PickleHTMLBuilder(StandaloneHTMLBuilder):
name = 'pickle'
out_suffix = '.fpickle'
indexer_format = 'pickle'
supported_image_types = ('image/svg+xml', 'image/png', 'image/gif',
'image/jpeg')
def init(self):
self.init_translator_class()
@ -711,6 +741,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
# don't copy the reST source
copysource = False
supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
def init(self):
StandaloneHTMLBuilder.init(self)
@ -726,6 +757,8 @@ class LaTeXBuilder(Builder):
Builds LaTeX output to create PDF.
"""
name = 'latex'
supported_image_types = ['application/pdf', 'image/png', 'image/gif',
'image/jpeg']
def init(self):
self.docnames = []
@ -787,6 +820,7 @@ class LaTeXBuilder(Builder):
self.info("processing " + targetname + "... ", nonl=1)
doctree = self.assemble_doctree(docname, toctree_only,
appendices=(docclass == 'manual') and appendices or [])
self.post_process_images(doctree)
self.info("writing... ", nonl=1)
doctree.settings = docsettings
doctree.settings.author = author
@ -852,9 +886,9 @@ class LaTeXBuilder(Builder):
def finish(self):
# copy image files
if self.env.images:
if self.images:
self.info(bold('copying images...'), nonl=1)
for src, (_, dest) in self.env.images.iteritems():
for src, dest in self.images.iteritems():
self.info(' '+src, nonl=1)
shutil.copyfile(path.join(self.srcdir, src),
path.join(self.outdir, dest))

View File

@ -14,9 +14,11 @@ import os
import time
import heapq
import types
import imghdr
import difflib
import cPickle as pickle
from os import path
from glob import glob
from string import uppercase
from itertools import izip, groupby
try:
@ -511,25 +513,43 @@ class BuildEnvironment:
existing_names = set(v[1] for v in self.images.itervalues())
docdir = path.dirname(self.doc2path(docname, base=None))
for node in doctree.traverse(nodes.image):
# Map the mimetype to the corresponding image. The writer may
# choose the best image from these candidates. The special key * is
# set if there is only single candiate to be used by a writer.
node['candidates'] = candidates = {}
imguri = node['uri']
if imguri.find('://') != -1:
self.warn(docname, 'Nonlocal image URI found: %s' % imguri, node.line)
else:
imgpath = path.normpath(path.join(docdir, imguri))
node['uri'] = imgpath
self.dependencies.setdefault(docname, set()).add(imgpath)
if not os.access(path.join(self.srcdir, imgpath), os.R_OK):
self.warn(docname, 'Image file not readable: %s' % imguri, node.line)
if imgpath in self.images:
self.images[imgpath][0].add(docname)
candidates['*'] = imguri
continue
uniquename = path.basename(imgpath)
imgpath = path.normpath(path.join(docdir, imguri))
if imgpath.endswith(os.extsep + '*'):
for filename in glob(imgpath):
basename, ext = os.path.splitext(filename)
if ext == '.pdf':
candidates['application/pdf'] = filename
elif ext == '.svg':
candidates['image/svg+xml'] = filename
else:
imgtype = imghdr.what(filename)
if imgtype:
candidates['image/' + imgtype] = filename
else:
candidates['*'] = imgpath
for img in candidates.itervalues():
self.dependencies.setdefault(docname, set()).add(img)
if not os.access(path.join(self.srcdir, img), os.R_OK):
self.warn(docname, 'Image file not readable: %s' % img, node.line)
if img in self.images:
self.images[img][0].add(docname)
continue
uniquename = path.basename(img)
base, ext = path.splitext(uniquename)
i = 0
while uniquename in existing_names:
i += 1
uniquename = '%s%s%s' % (base, i, ext)
self.images[imgpath] = (set([docname]), uniquename)
self.images[img] = (set([docname]), uniquename)
existing_names.add(uniquename)
def process_metadata(self, docname, doctree):

View File

@ -250,9 +250,9 @@ class HTMLTranslator(BaseTranslator):
def visit_image(self, node):
olduri = node['uri']
# rewrite the URI if the environment knows about it
if olduri in self.builder.env.images:
if olduri in self.builder.images:
node['uri'] = posixpath.join(self.builder.imgpath,
self.builder.env.images[olduri][1])
self.builder.images[olduri])
BaseTranslator.visit_image(self, node)
def visit_toctree(self, node):

View File

@ -630,8 +630,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
pre.append('\n')
post.append('\n')
pre.reverse()
if node['uri'] in self.builder.env.images:
uri = self.builder.env.images[node['uri']][1]
if node['uri'] in self.builder.images:
uri = self.builder.images[node['uri']]
else:
uri = node['uri']
if uri.find('://') != -1: