Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/nbconvert/preprocessors/svg2pdf.py: 34%
80 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1"""Module containing a preprocessor that converts outputs in the notebook from
2one format to another.
3"""
5# Copyright (c) Jupyter Development Team.
6# Distributed under the terms of the Modified BSD License.
8import base64
9import os
10import subprocess
11import sys
12from shutil import which
13from tempfile import TemporaryDirectory
15from traitlets import List, Unicode, Union, default
17from nbconvert.utils.io import FormatSafeDict
19from .convertfigures import ConvertFiguresPreprocessor
21# inkscape path for darwin (macOS)
22INKSCAPE_APP = "/Applications/Inkscape.app/Contents/Resources/bin/inkscape"
23# Recent versions of Inkscape (v1.0) moved the executable from
24# Resources/bin/inkscape to MacOS/inkscape
25INKSCAPE_APP_v1 = "/Applications/Inkscape.app/Contents/MacOS/inkscape"
27if sys.platform == "win32":
28 try:
29 import winreg
30 except ImportError:
31 import _winreg as winreg
34class SVG2PDFPreprocessor(ConvertFiguresPreprocessor):
35 """
36 Converts all of the outputs in a notebook from SVG to PDF.
37 """
39 @default("from_format")
40 def _from_format_default(self):
41 return "image/svg+xml"
43 @default("to_format")
44 def _to_format_default(self):
45 return "application/pdf"
47 inkscape_version = Unicode(
48 help="""The version of inkscape being used.
50 This affects how the conversion command is run.
51 """
52 ).tag(config=True)
54 @default("inkscape_version")
55 def _inkscape_version_default(self):
56 p = subprocess.Popen(
57 [self.inkscape, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE # noqa
58 )
59 output, _ = p.communicate()
60 if p.returncode != 0:
61 msg = "Unable to find inkscape executable --version"
62 raise RuntimeError(msg)
63 return output.decode("utf-8").split(" ")[1]
65 # FIXME: Deprecate passing a string here
66 command = Union(
67 [Unicode(), List()],
68 help="""
69 The command to use for converting SVG to PDF
71 This traitlet is a template, which will be formatted with the keys
72 to_filename and from_filename.
74 The conversion call must read the SVG from {from_filename},
75 and write a PDF to {to_filename}.
77 It could be a List (recommended) or a String. If string, it will
78 be passed to a shell for execution.
79 """,
80 ).tag(config=True)
82 @default("command")
83 def _command_default(self):
84 major_version = self.inkscape_version.split(".")[0]
85 command = [self.inkscape]
87 if int(major_version) < 1:
88 # --without-gui is only needed for inkscape 0.x
89 command.append("--without-gui")
90 # --export-pdf is old name for --export-filename
91 command.append("--export-pdf={to_filename}")
92 else:
93 command.append("--export-filename={to_filename}")
95 command.append("{from_filename}")
96 return command
98 inkscape = Unicode(help="The path to Inkscape, if necessary").tag(config=True)
100 @default("inkscape")
101 def _inkscape_default(self):
102 inkscape_path = which("inkscape")
103 if inkscape_path is not None:
104 return inkscape_path
105 if sys.platform == "darwin":
106 if os.path.isfile(INKSCAPE_APP_v1):
107 return INKSCAPE_APP_v1
108 # Order is important. If INKSCAPE_APP exists, prefer it over
109 # the executable in the MacOS directory.
110 if os.path.isfile(INKSCAPE_APP):
111 return INKSCAPE_APP
112 if sys.platform == "win32":
113 wr_handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
114 try:
115 rkey = winreg.OpenKey(wr_handle, "SOFTWARE\\Classes\\inkscape.svg\\DefaultIcon")
116 inkscape = winreg.QueryValueEx(rkey, "")[0]
117 except FileNotFoundError:
118 msg = "Inkscape executable not found"
119 raise FileNotFoundError(msg) from None
120 return inkscape
121 return "inkscape"
123 def convert_figure(self, data_format, data):
124 """
125 Convert a single SVG figure to PDF. Returns converted data.
126 """
128 # Work in a temporary directory
129 with TemporaryDirectory() as tmpdir:
130 # Write fig to temp file
131 input_filename = os.path.join(tmpdir, "figure.svg")
132 # SVG data is unicode text
133 with open(input_filename, "w", encoding="utf8") as f:
134 f.write(data)
136 # Call conversion application
137 output_filename = os.path.join(tmpdir, "figure.pdf")
139 template_vars = {"from_filename": input_filename, "to_filename": output_filename}
140 if isinstance(self.command, list):
141 full_cmd = [s.format_map(FormatSafeDict(**template_vars)) for s in self.command]
142 else:
143 # For backwards compatibility with specifying strings
144 # Okay-ish, since the string is trusted
145 full_cmd = self.command.format(*template_vars)
146 subprocess.call(full_cmd, shell=isinstance(full_cmd, str)) # noqa
148 # Read output from drive
149 # return value expects a filename
150 if os.path.isfile(output_filename):
151 with open(output_filename, "rb") as f:
152 # PDF is a nb supported binary, data type, so base64 encode.
153 return base64.encodebytes(f.read()).decode("utf-8")
154 else:
155 msg = "Inkscape svg to pdf conversion failed"
156 raise TypeError(msg)