mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
152 lines
6.2 KiB
Python
152 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
sphinx.environment.collectors.asset
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The image collector for sphinx.environment.
|
|
|
|
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
import os
|
|
from glob import glob
|
|
from os import path
|
|
|
|
from docutils import nodes
|
|
from docutils.utils import relative_path
|
|
|
|
from sphinx import addnodes
|
|
from sphinx.environment.collectors import EnvironmentCollector
|
|
from sphinx.locale import __
|
|
from sphinx.util import logging
|
|
from sphinx.util.i18n import get_image_filename_for_language, search_image_for_language
|
|
from sphinx.util.images import guess_mimetype
|
|
|
|
if False:
|
|
# For type annotation
|
|
from typing import Dict, List, Set, Tuple # NOQA
|
|
from docutils import nodes # NOQA
|
|
from sphinx.sphinx import Sphinx # NOQA
|
|
from sphinx.environment import BuildEnvironment # NOQA
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ImageCollector(EnvironmentCollector):
|
|
"""Image files collector for sphinx.environment."""
|
|
|
|
def clear_doc(self, app, env, docname):
|
|
# type: (Sphinx, BuildEnvironment, str) -> None
|
|
env.images.purge_doc(docname)
|
|
|
|
def merge_other(self, app, env, docnames, other):
|
|
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
|
|
env.images.merge_other(docnames, other.images)
|
|
|
|
def process_doc(self, app, doctree):
|
|
# type: (Sphinx, nodes.document) -> None
|
|
"""Process and rewrite image URIs."""
|
|
docname = app.env.docname
|
|
|
|
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 candidate to be used by a writer.
|
|
# The special key ? is set for nonlocal URIs.
|
|
candidates = {} # type: Dict[str, str]
|
|
node['candidates'] = candidates
|
|
imguri = node['uri']
|
|
if imguri.startswith('data:'):
|
|
candidates['?'] = imguri
|
|
continue
|
|
elif imguri.find('://') != -1:
|
|
candidates['?'] = imguri
|
|
continue
|
|
rel_imgpath, full_imgpath = app.env.relfn2path(imguri, docname)
|
|
if app.config.language:
|
|
# substitute figures (ex. foo.png -> foo.en.png)
|
|
i18n_full_imgpath = search_image_for_language(full_imgpath, app.env)
|
|
if i18n_full_imgpath != full_imgpath:
|
|
full_imgpath = i18n_full_imgpath
|
|
rel_imgpath = relative_path(path.join(app.srcdir, 'dummy'),
|
|
i18n_full_imgpath)
|
|
# set imgpath as default URI
|
|
node['uri'] = rel_imgpath
|
|
if rel_imgpath.endswith(os.extsep + '*'):
|
|
if app.config.language:
|
|
# Search language-specific figures at first
|
|
i18n_imguri = get_image_filename_for_language(imguri, app.env)
|
|
_, full_i18n_imgpath = app.env.relfn2path(i18n_imguri, docname)
|
|
self.collect_candidates(app.env, full_i18n_imgpath, candidates, node)
|
|
|
|
self.collect_candidates(app.env, full_imgpath, candidates, node)
|
|
else:
|
|
candidates['*'] = rel_imgpath
|
|
|
|
# map image paths to unique image names (so that they can be put
|
|
# into a single directory)
|
|
for imgpath in candidates.values():
|
|
app.env.dependencies[docname].add(imgpath)
|
|
if not os.access(path.join(app.srcdir, imgpath), os.R_OK):
|
|
logger.warning(__('image file not readable: %s') % imgpath,
|
|
location=node, type='image', subtype='not_readable')
|
|
continue
|
|
app.env.images.add_file(docname, imgpath)
|
|
|
|
def collect_candidates(self, env, imgpath, candidates, node):
|
|
# type: (BuildEnvironment, str, Dict[str, str], nodes.Node) -> None
|
|
globbed = {} # type: Dict[str, List[str]]
|
|
for filename in glob(imgpath):
|
|
new_imgpath = relative_path(path.join(env.srcdir, 'dummy'),
|
|
filename)
|
|
try:
|
|
mimetype = guess_mimetype(filename)
|
|
if mimetype not in candidates:
|
|
globbed.setdefault(mimetype, []).append(new_imgpath)
|
|
except (OSError, IOError) as err:
|
|
logger.warning(__('image file %s not readable: %s') % (filename, err),
|
|
location=node, type='image', subtype='not_readable')
|
|
for key, files in globbed.items():
|
|
candidates[key] = sorted(files, key=len)[0] # select by similarity
|
|
|
|
|
|
class DownloadFileCollector(EnvironmentCollector):
|
|
"""Download files collector for sphinx.environment."""
|
|
|
|
def clear_doc(self, app, env, docname):
|
|
# type: (Sphinx, BuildEnvironment, str) -> None
|
|
env.dlfiles.purge_doc(docname)
|
|
|
|
def merge_other(self, app, env, docnames, other):
|
|
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
|
|
env.dlfiles.merge_other(docnames, other.dlfiles)
|
|
|
|
def process_doc(self, app, doctree):
|
|
# type: (Sphinx, nodes.document) -> None
|
|
"""Process downloadable file paths. """
|
|
for node in doctree.traverse(addnodes.download_reference):
|
|
targetname = node['reftarget']
|
|
if '://' in targetname:
|
|
node['refuri'] = targetname
|
|
else:
|
|
rel_filename, filename = app.env.relfn2path(targetname, app.env.docname)
|
|
app.env.dependencies[app.env.docname].add(rel_filename)
|
|
if not os.access(filename, os.R_OK):
|
|
logger.warning(__('download file not readable: %s') % filename,
|
|
location=node, type='download', subtype='not_readable')
|
|
continue
|
|
node['filename'] = app.env.dlfiles.add_file(app.env.docname, filename)
|
|
|
|
|
|
def setup(app):
|
|
# type: (Sphinx) -> Dict
|
|
app.add_env_collector(ImageCollector)
|
|
app.add_env_collector(DownloadFileCollector)
|
|
|
|
return {
|
|
'version': 'builtin',
|
|
'parallel_read_safe': True,
|
|
'parallel_write_safe': True,
|
|
}
|