Support images in Data URI on non-HTML builders

This commit is contained in:
Takeshi KOMIYA 2017-04-19 01:38:30 +09:00
parent be261ed71e
commit ebdec70dfc
6 changed files with 47 additions and 8 deletions

View File

@ -106,7 +106,8 @@ Features added
``suppress_warnings`` ``suppress_warnings``
* #2803: Discovery of builders by entry point * #2803: Discovery of builders by entry point
* #1764, #1676: Allow setting 'rel' and 'title' attributes for stylesheets * #1764, #1676: Allow setting 'rel' and 'title' attributes for stylesheets
* #3589: Support remote images * #3589: Support remote images on non-HTML builders
* #3589: Support images in Data URI on non-HTML builders
Bugs fixed Bugs fixed
---------- ----------

View File

@ -227,7 +227,6 @@ General configuration
* app.add_generic_role * app.add_generic_role
* app.add_source_parser * app.add_source_parser
* download.not_readable * download.not_readable
* image.data_uri
* image.not_readable * image.not_readable
* ref.term * ref.term
* ref.ref * ref.ref

View File

@ -65,6 +65,7 @@ class Builder(object):
#: Image files are searched in the order in which they appear here. #: Image files are searched in the order in which they appear here.
supported_image_types = [] # type: List[unicode] supported_image_types = [] # type: List[unicode]
supported_remote_images = True supported_remote_images = True
supported_data_uri_images = False
def __init__(self, app): def __init__(self, app):
# type: (Sphinx) -> None # type: (Sphinx) -> None

View File

@ -103,6 +103,7 @@ class StandaloneHTMLBuilder(Builder):
html_scaled_image_link = True html_scaled_image_link = True
supported_image_types = ['image/svg+xml', 'image/png', supported_image_types = ['image/svg+xml', 'image/png',
'image/gif', 'image/jpeg'] 'image/gif', 'image/jpeg']
supported_data_uri_images = True
searchindex_filename = 'searchindex.js' searchindex_filename = 'searchindex.js'
add_permalinks = True add_permalinks = True
allow_sharp_as_current_path = True allow_sharp_as_current_path = True

View File

@ -59,8 +59,6 @@ class ImageCollector(EnvironmentCollector):
node['candidates'] = candidates node['candidates'] = candidates
imguri = node['uri'] imguri = node['uri']
if imguri.startswith('data:'): if imguri.startswith('data:'):
logger.warning('image data URI found. some builders might not support',
location=node, type='image', subtype='data_uri')
candidates['?'] = imguri candidates['?'] = imguri
continue continue
elif imguri.find('://') != -1: elif imguri.find('://') != -1:

View File

@ -10,13 +10,14 @@
""" """
import os import os
from hashlib import sha1
from six import text_type from six import text_type
from docutils import nodes from docutils import nodes
from sphinx.transforms import SphinxTransform from sphinx.transforms import SphinxTransform
from sphinx.util import logging, requests from sphinx.util import logging, requests
from sphinx.util.images import guess_mimetype from sphinx.util.images import guess_mimetype, get_image_extension, parse_data_uri
from sphinx.util.osutil import ensuredir from sphinx.util.osutil import ensuredir
if False: if False:
@ -43,6 +44,11 @@ class BaseImageConverter(SphinxTransform):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
pass pass
@property
def imagedir(self):
# type: () -> unicode
return os.path.join(self.app.doctreedir, 'images')
class ImageDownloader(BaseImageConverter): class ImageDownloader(BaseImageConverter):
default_priority = 100 default_priority = 100
@ -56,14 +62,13 @@ class ImageDownloader(BaseImageConverter):
def handle(self, node): def handle(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
imgdir = os.path.join(self.app.doctreedir, 'images')
basename = os.path.basename(node['uri']) basename = os.path.basename(node['uri'])
if '?' in basename: if '?' in basename:
basename = basename.split('?')[0] basename = basename.split('?')[0]
dirname = node['uri'].replace('://', '/').translate({ord("?"): u"/", dirname = node['uri'].replace('://', '/').translate({ord("?"): u"/",
ord("&"): u"/"}) ord("&"): u"/"})
ensuredir(os.path.join(imgdir, dirname)) ensuredir(os.path.join(self.imagedir, dirname))
path = os.path.join(imgdir, dirname, basename) path = os.path.join(self.imagedir, dirname, basename)
try: try:
r = requests.get(node['uri']) r = requests.get(node['uri'])
if r.status_code != 200: if r.status_code != 200:
@ -85,9 +90,43 @@ class ImageDownloader(BaseImageConverter):
(node['uri'], text_type(exc))) (node['uri'], text_type(exc)))
class DataURIExtractor(BaseImageConverter):
default_priority = 150
def match(self, node):
# type: (nodes.Node) -> bool
if self.app.builder.supported_data_uri_images:
return False
else:
return 'data:' in node['uri']
def handle(self, node):
# type: (nodes.Node) -> None
image = parse_data_uri(node['uri'])
ext = get_image_extension(image.mimetype)
if ext is None:
logger.warning('Unknown image format: %s...', node['uri'][:32],
location=node)
return
ensuredir(os.path.join(self.imagedir, 'embeded'))
digest = sha1(image.data).hexdigest()
path = os.path.join(self.imagedir, 'embeded', digest + ext)
self.app.env.original_image_uri[path] = node['uri']
with open(path, 'wb') as f:
f.write(image.data)
node['candidates'].pop('?')
node['candidates'][image.mimetype] = path
node['uri'] = path
self.app.env.images.add_file(self.env.docname, path)
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]
app.add_post_transform(ImageDownloader) app.add_post_transform(ImageDownloader)
app.add_post_transform(DataURIExtractor)
return { return {
'version': 'builtin', 'version': 'builtin',