1""" 2 sphinx.ext.imgconverter 3 ~~~~~~~~~~~~~~~~~~~~~~~ 4 5 Image converter extension for Sphinx 6 7 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 :license: BSD, see LICENSE for details. 9""" 10 11import subprocess 12import sys 13from subprocess import PIPE, CalledProcessError 14from typing import Any, Dict 15 16from sphinx.application import Sphinx 17from sphinx.errors import ExtensionError 18from sphinx.locale import __ 19from sphinx.transforms.post_transforms.images import ImageConverter 20from sphinx.util import logging 21 22logger = logging.getLogger(__name__) 23 24 25class ImagemagickConverter(ImageConverter): 26 conversion_rules = [ 27 ('image/svg+xml', 'image/png'), 28 ('image/gif', 'image/png'), 29 ('application/pdf', 'image/png'), 30 ('application/illustrator', 'image/png'), 31 ] 32 33 def is_available(self) -> bool: 34 """Confirms the converter is available or not.""" 35 try: 36 args = [self.config.image_converter, '-version'] 37 logger.debug('Invoking %r ...', args) 38 subprocess.run(args, stdout=PIPE, stderr=PIPE, check=True) 39 return True 40 except OSError: 41 logger.warning(__('convert command %r cannot be run, ' 42 'check the image_converter setting'), 43 self.config.image_converter) 44 return False 45 except CalledProcessError as exc: 46 logger.warning(__('convert exited with error:\n' 47 '[stderr]\n%r\n[stdout]\n%r'), 48 exc.stderr, exc.stdout) 49 return False 50 51 def convert(self, _from: str, _to: str) -> bool: 52 """Converts the image to expected one.""" 53 try: 54 # append an index 0 to source filename to pick up the first frame 55 # (or first page) of image (ex. Animation GIF, PDF) 56 _from += '[0]' 57 58 args = ([self.config.image_converter] + 59 self.config.image_converter_args + 60 [_from, _to]) 61 logger.debug('Invoking %r ...', args) 62 subprocess.run(args, stdout=PIPE, stderr=PIPE, check=True) 63 return True 64 except OSError: 65 logger.warning(__('convert command %r cannot be run, ' 66 'check the image_converter setting'), 67 self.config.image_converter) 68 return False 69 except CalledProcessError as exc: 70 raise ExtensionError(__('convert exited with error:\n' 71 '[stderr]\n%r\n[stdout]\n%r') % 72 (exc.stderr, exc.stdout)) from exc 73 74 75def setup(app: Sphinx) -> Dict[str, Any]: 76 app.add_post_transform(ImagemagickConverter) 77 if sys.platform == 'win32': 78 # On Windows, we use Imagemagik v7 by default to avoid the trouble for 79 # convert.exe bundled with Windows. 80 app.add_config_value('image_converter', 'magick', 'env') 81 app.add_config_value('image_converter_args', ['convert'], 'env') 82 else: 83 # On other platform, we use Imagemagick v6 by default. Especially, 84 # Debian/Ubuntu are still based of v6. So we can't use "magick" command 85 # for these platforms. 86 app.add_config_value('image_converter', 'convert', 'env') 87 app.add_config_value('image_converter_args', [], 'env') 88 89 return { 90 'version': 'builtin', 91 'parallel_read_safe': True, 92 'parallel_write_safe': True, 93 } 94