Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LaTeX SVG to PDF converter extension #4772

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions sphinx/ext/latex_svg2pdf_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
"""
sphinx.ext.latex_svg2pdf_converter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SVG to PDF converter extension for LaTeX builder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only for LaTeX builder?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTML, ePUB and so on handle SVG fine (if the viewer supports it, but that's another story). AFAIK LaTeX is the only one that needs mandatory conversion to PDF. However we could provide a configuration option to fine-tune conversion rules.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ImageConveter will be called only if the builder does not support the image format.
For example, HTML builder supports image/svg+xml format. So this converter will not be called on that case.

supported_image_types = ['image/svg+xml', 'image/png',
'image/gif', 'image/jpeg']

On the other hand, LaTeX builder does not support it. But it supports application/pdf.

supported_image_types = ['application/pdf', 'image/png', 'image/jpeg']

ImageConverter class and its subclass will be called based on conversion_rules mapping.

+    conversion_rules = [
+        ('image/svg+xml', 'application/pdf'),
+    ]

This mapping table consists of list of (source format, destination format).

For this reason, you don't need to restrict this only for LaTeX builder.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're completely right. For some reason I must have missed that…


:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import subprocess

from sphinx.errors import ExtensionError
from sphinx.locale import __
from sphinx.transforms.post_transforms.images import ImageConverter
from sphinx.util import logging
from sphinx.util.osutil import ENOENT, EPIPE, EINVAL

if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA


logger = logging.getLogger(__name__)


class InkscapeConverter(ImageConverter):
conversion_rules = [
('image/svg+xml', 'application/pdf'),
]

def is_available(self):
# type: () -> bool
"""Confirms the converter is available or not."""
try:
args = [self.config.latex_svg2pdf_converter, '--version']
logger.debug('Invoking %r ...', args)
ret = subprocess.call(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
if ret == 0:
return True
else:
return False
except (OSError, IOError):
logger.warning(__('convert command %r cannot be run.'
'check the image_converter setting'),
self.latex_svg2pdf_converter)
return False

def convert(self, _from, _to):
# type: (unicode, unicode) -> bool
"""Converts the image to expected one."""
if self.app.builder.name != 'latex':
return

try:
args = ([self.config.latex_svg2pdf_converter] +
self.config.latex_svg2pdf_converter_args +
['--export-pdf=' + _to, _from])
logger.debug('Invoking %r ...', args)
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
except OSError as err:
if err.errno != ENOENT: # No such file or directory
raise
logger.warning(__('convert command %r cannot be run.'
'check the image_converter setting'),
self.config.latex_svg2pdf_converter)
return False

try:
stdout, stderr = p.communicate()
except (OSError, IOError) as err:
if err.errno not in (EPIPE, EINVAL):
raise
stdout, stderr = p.stdout.read(), p.stderr.read()
p.wait()
if p.returncode != 0:
raise ExtensionError(__('convert exited with error:\n'
'[stderr]\n%s\n[stdout]\n%s') %
(stderr, stdout))

return True


def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_post_transform(InkscapeConverter)
app.add_config_value('latex_svg2pdf_converter', 'inkscape', 'env')
app.add_config_value('latex_svg2pdf_converter_args',
['--export-area-drawing'], 'env')

return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}