Merge branch 'pdf-conversion' into docs-next
PDF-generation improvements from Akira Yokasawa; Akira says: This patch set improves conversions of DOT -> PDF and SVG -> PDF for PDF docs. * DOT -> PDF conversion Current scheme uses "dot -Tpdf" (of graphviz). Cons: - openSUSE's dot(1) does not support -Tpdf. - Other distro's dot(1) generates PDFs with unnecessarily wide margins for inclusion into LaTeX docs. Patch 1/4 changes the route to the following two steps: 1. DOT -> SVG by "dot -Tsvg" 2. SVG -> PDF by "rsvg-convert -f pdf" with fallback to convert(1) Pros: - Improved portability across distros - Less space around graphs in final PDF documents Con: - On systems without rsvg-convert, generated PDF will be of raster image. Patch 2/4 avoids raster-image PDF by using "dot -Tpdf" on systems where the option is available. * SVG -> PDF conversion Current scheme uses convert(1) (of ImageMagick) Cons: - Generated PDFs are of raster image. Some of them look blurry. - Raster images tend to be large in size. - convert(1) delegates SVG decoding to rsvg-convert(1). It doesn't cover full range of Inkscape-specific SVG features and fails to convert some of SVG figures properly. Improper conversions are observed with SVGs listed below (incomplete, conversion quality depends on the version of rsvg-convert): - Documentation/userspace-api/media/v4l/selection.svg - Documentation/userspace-api/media/v4l/vbi_525.svg - Documentation/userspace-api/media/v4l/vbi_625.svg - Documentation/userspace-api/media/v4l/vbi_hsync.svg - Documentation/admin-guide/blockdev/drbd/DRBD-8.3-data-packets.svg - Documentation/admin-guide/blockdev/drbd/DRBD-data-packages.svg If you have Inkscape installed as well, convert(1) delegates SVG decoding to inkscape(1) rather than to rsvg-convert(1) and SVGs listed above can be rendered properly. So if Inkscape is required for converting those SVGs properly, why not use it directly in the first place? Patches 3/4 and 4/4 add code to utilize inkscape(1) for SVG -> PDF conversion when it is available. They don't modify any existing requirements for kernel-doc. Patch 3/4 adds the alternative route of SVG -> PDF conversion by inkscape(1). Patch 4/4 delegates warning messages from inkscape(1) to kernellog.verbose as they are likely harmless in command-line uses. Pros: - Generated PDFs are of vector graphics. - Vector graphics tends to be smaller in size and looks nicer when zoomed in. - SVGs drawn by Inkscape are fully supported. On systems without Inkscape, no regression is expected by these two patches.
This commit is contained in:
commit
f647de4b02
@ -31,10 +31,13 @@ u"""
|
||||
|
||||
* ``dot(1)``: Graphviz (https://www.graphviz.org). If Graphviz is not
|
||||
available, the DOT language is inserted as literal-block.
|
||||
For conversion to PDF, ``rsvg-convert(1)`` of librsvg
|
||||
(https://gitlab.gnome.org/GNOME/librsvg) is used when available.
|
||||
|
||||
* SVG to PDF: To generate PDF, you need at least one of this tools:
|
||||
|
||||
- ``convert(1)``: ImageMagick (https://www.imagemagick.org)
|
||||
- ``inkscape(1)``: Inkscape (https://inkscape.org/)
|
||||
|
||||
List of customizations:
|
||||
|
||||
@ -49,6 +52,7 @@ import os
|
||||
from os import path
|
||||
import subprocess
|
||||
from hashlib import sha1
|
||||
import re
|
||||
from docutils import nodes
|
||||
from docutils.statemachine import ViewList
|
||||
from docutils.parsers.rst import directives
|
||||
@ -109,10 +113,20 @@ def pass_handle(self, node): # pylint: disable=W0613
|
||||
|
||||
# Graphviz's dot(1) support
|
||||
dot_cmd = None
|
||||
# dot(1) -Tpdf should be used
|
||||
dot_Tpdf = False
|
||||
|
||||
# ImageMagick' convert(1) support
|
||||
convert_cmd = None
|
||||
|
||||
# librsvg's rsvg-convert(1) support
|
||||
rsvg_convert_cmd = None
|
||||
|
||||
# Inkscape's inkscape(1) support
|
||||
inkscape_cmd = None
|
||||
# Inkscape prior to 1.0 uses different command options
|
||||
inkscape_ver_one = False
|
||||
|
||||
|
||||
def setup(app):
|
||||
# check toolchain first
|
||||
@ -160,23 +174,62 @@ def setupTools(app):
|
||||
|
||||
This function is called once, when the builder is initiated.
|
||||
"""
|
||||
global dot_cmd, convert_cmd # pylint: disable=W0603
|
||||
global dot_cmd, dot_Tpdf, convert_cmd, rsvg_convert_cmd # pylint: disable=W0603
|
||||
global inkscape_cmd, inkscape_ver_one # pylint: disable=W0603
|
||||
kernellog.verbose(app, "kfigure: check installed tools ...")
|
||||
|
||||
dot_cmd = which('dot')
|
||||
convert_cmd = which('convert')
|
||||
rsvg_convert_cmd = which('rsvg-convert')
|
||||
inkscape_cmd = which('inkscape')
|
||||
|
||||
if dot_cmd:
|
||||
kernellog.verbose(app, "use dot(1) from: " + dot_cmd)
|
||||
|
||||
try:
|
||||
dot_Thelp_list = subprocess.check_output([dot_cmd, '-Thelp'],
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as err:
|
||||
dot_Thelp_list = err.output
|
||||
pass
|
||||
|
||||
dot_Tpdf_ptn = b'pdf'
|
||||
dot_Tpdf = re.search(dot_Tpdf_ptn, dot_Thelp_list)
|
||||
else:
|
||||
kernellog.warn(app, "dot(1) not found, for better output quality install "
|
||||
"graphviz from https://www.graphviz.org")
|
||||
if convert_cmd:
|
||||
kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
|
||||
if inkscape_cmd:
|
||||
kernellog.verbose(app, "use inkscape(1) from: " + inkscape_cmd)
|
||||
inkscape_ver = subprocess.check_output([inkscape_cmd, '--version'],
|
||||
stderr=subprocess.DEVNULL)
|
||||
ver_one_ptn = b'Inkscape 1'
|
||||
inkscape_ver_one = re.search(ver_one_ptn, inkscape_ver)
|
||||
convert_cmd = None
|
||||
rsvg_convert_cmd = None
|
||||
dot_Tpdf = False
|
||||
|
||||
else:
|
||||
kernellog.warn(app,
|
||||
"convert(1) not found, for SVG to PDF conversion install "
|
||||
"ImageMagick (https://www.imagemagick.org)")
|
||||
if convert_cmd:
|
||||
kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
|
||||
else:
|
||||
kernellog.warn(app,
|
||||
"Neither inkscape(1) nor convert(1) found.\n"
|
||||
"For SVG to PDF conversion, "
|
||||
"install either Inkscape (https://inkscape.org/) (preferred) or\n"
|
||||
"ImageMagick (https://www.imagemagick.org)")
|
||||
|
||||
if rsvg_convert_cmd:
|
||||
kernellog.verbose(app, "use rsvg-convert(1) from: " + rsvg_convert_cmd)
|
||||
kernellog.verbose(app, "use 'dot -Tsvg' and rsvg-convert(1) for DOT -> PDF conversion")
|
||||
dot_Tpdf = False
|
||||
else:
|
||||
kernellog.verbose(app,
|
||||
"rsvg-convert(1) not found.\n"
|
||||
" SVG rendering of convert(1) is done by ImageMagick-native renderer.")
|
||||
if dot_Tpdf:
|
||||
kernellog.verbose(app, "use 'dot -Tpdf' for DOT -> PDF conversion")
|
||||
else:
|
||||
kernellog.verbose(app, "use 'dot -Tsvg' and convert(1) for DOT -> PDF conversion")
|
||||
|
||||
|
||||
# integrate conversion tools
|
||||
@ -242,7 +295,7 @@ def convert_image(img_node, translator, src_fname=None):
|
||||
elif in_ext == '.svg':
|
||||
|
||||
if translator.builder.format == 'latex':
|
||||
if convert_cmd is None:
|
||||
if not inkscape_cmd and convert_cmd is None:
|
||||
kernellog.verbose(app,
|
||||
"no SVG to PDF conversion available / include SVG raw.")
|
||||
img_node.replace_self(file2literal(src_fname))
|
||||
@ -266,7 +319,14 @@ def convert_image(img_node, translator, src_fname=None):
|
||||
|
||||
if in_ext == '.dot':
|
||||
kernellog.verbose(app, 'convert DOT to: {out}/' + _name)
|
||||
ok = dot2format(app, src_fname, dst_fname)
|
||||
if translator.builder.format == 'latex' and not dot_Tpdf:
|
||||
svg_fname = path.join(translator.builder.outdir, fname + '.svg')
|
||||
ok1 = dot2format(app, src_fname, svg_fname)
|
||||
ok2 = svg2pdf_by_rsvg(app, svg_fname, dst_fname)
|
||||
ok = ok1 and ok2
|
||||
|
||||
else:
|
||||
ok = dot2format(app, src_fname, dst_fname)
|
||||
|
||||
elif in_ext == '.svg':
|
||||
kernellog.verbose(app, 'convert SVG to: {out}/' + _name)
|
||||
@ -303,22 +363,70 @@ def dot2format(app, dot_fname, out_fname):
|
||||
return bool(exit_code == 0)
|
||||
|
||||
def svg2pdf(app, svg_fname, pdf_fname):
|
||||
"""Converts SVG to PDF with ``convert(1)`` command.
|
||||
"""Converts SVG to PDF with ``inkscape(1)`` or ``convert(1)`` command.
|
||||
|
||||
Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for
|
||||
conversion. Returns ``True`` on success and ``False`` if an error occurred.
|
||||
Uses ``inkscape(1)`` from Inkscape (https://inkscape.org/) or ``convert(1)``
|
||||
from ImageMagick (https://www.imagemagick.org) for conversion.
|
||||
Returns ``True`` on success and ``False`` if an error occurred.
|
||||
|
||||
* ``svg_fname`` pathname of the input SVG file with extension (``.svg``)
|
||||
* ``pdf_name`` pathname of the output PDF file with extension (``.pdf``)
|
||||
|
||||
"""
|
||||
cmd = [convert_cmd, svg_fname, pdf_fname]
|
||||
# use stdout and stderr from parent
|
||||
exit_code = subprocess.call(cmd)
|
||||
cmd_name = 'convert(1)'
|
||||
|
||||
if inkscape_cmd:
|
||||
cmd_name = 'inkscape(1)'
|
||||
if inkscape_ver_one:
|
||||
cmd = [inkscape_cmd, '-o', pdf_fname, svg_fname]
|
||||
else:
|
||||
cmd = [inkscape_cmd, '-z', '--export-pdf=%s' % pdf_fname, svg_fname]
|
||||
|
||||
try:
|
||||
warning_msg = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
exit_code = 0
|
||||
except subprocess.CalledProcessError as err:
|
||||
warning_msg = err.output
|
||||
exit_code = err.returncode
|
||||
pass
|
||||
|
||||
if exit_code != 0:
|
||||
kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
|
||||
if warning_msg:
|
||||
kernellog.warn(app, "Warning msg from %s: %s"
|
||||
% (cmd_name, str(warning_msg, 'utf-8')))
|
||||
elif warning_msg:
|
||||
kernellog.verbose(app, "Warning msg from %s (likely harmless):\n%s"
|
||||
% (cmd_name, str(warning_msg, 'utf-8')))
|
||||
|
||||
return bool(exit_code == 0)
|
||||
|
||||
def svg2pdf_by_rsvg(app, svg_fname, pdf_fname):
|
||||
"""Convert SVG to PDF with ``rsvg-convert(1)`` command.
|
||||
|
||||
* ``svg_fname`` pathname of input SVG file, including extension ``.svg``
|
||||
* ``pdf_fname`` pathname of output PDF file, including extension ``.pdf``
|
||||
|
||||
Input SVG file should be the one generated by ``dot2format()``.
|
||||
SVG -> PDF conversion is done by ``rsvg-convert(1)``.
|
||||
|
||||
If ``rsvg-convert(1)`` is unavailable, fall back to ``svg2pdf()``.
|
||||
|
||||
"""
|
||||
|
||||
if rsvg_convert_cmd is None:
|
||||
ok = svg2pdf(app, svg_fname, pdf_fname)
|
||||
else:
|
||||
cmd = [rsvg_convert_cmd, '--format=pdf', '-o', pdf_fname, svg_fname]
|
||||
# use stdout and stderr from parent
|
||||
exit_code = subprocess.call(cmd)
|
||||
if exit_code != 0:
|
||||
kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
|
||||
ok = bool(exit_code == 0)
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
# image handling
|
||||
# ---------------------
|
||||
|
Loading…
Reference in New Issue
Block a user