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

1"""Module containing a preprocessor that converts outputs in the notebook from 

2one format to another. 

3""" 

4 

5# Copyright (c) Jupyter Development Team. 

6# Distributed under the terms of the Modified BSD License. 

7 

8import base64 

9import os 

10import subprocess 

11import sys 

12from shutil import which 

13from tempfile import TemporaryDirectory 

14 

15from traitlets import List, Unicode, Union, default 

16 

17from nbconvert.utils.io import FormatSafeDict 

18 

19from .convertfigures import ConvertFiguresPreprocessor 

20 

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" 

26 

27if sys.platform == "win32": 

28 try: 

29 import winreg 

30 except ImportError: 

31 import _winreg as winreg 

32 

33 

34class SVG2PDFPreprocessor(ConvertFiguresPreprocessor): 

35 """ 

36 Converts all of the outputs in a notebook from SVG to PDF. 

37 """ 

38 

39 @default("from_format") 

40 def _from_format_default(self): 

41 return "image/svg+xml" 

42 

43 @default("to_format") 

44 def _to_format_default(self): 

45 return "application/pdf" 

46 

47 inkscape_version = Unicode( 

48 help="""The version of inkscape being used. 

49 

50 This affects how the conversion command is run. 

51 """ 

52 ).tag(config=True) 

53 

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] 

64 

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 

70 

71 This traitlet is a template, which will be formatted with the keys 

72 to_filename and from_filename. 

73 

74 The conversion call must read the SVG from {from_filename}, 

75 and write a PDF to {to_filename}. 

76 

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) 

81 

82 @default("command") 

83 def _command_default(self): 

84 major_version = self.inkscape_version.split(".")[0] 

85 command = [self.inkscape] 

86 

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}") 

94 

95 command.append("{from_filename}") 

96 return command 

97 

98 inkscape = Unicode(help="The path to Inkscape, if necessary").tag(config=True) 

99 

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" 

122 

123 def convert_figure(self, data_format, data): 

124 """ 

125 Convert a single SVG figure to PDF. Returns converted data. 

126 """ 

127 

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) 

135 

136 # Call conversion application 

137 output_filename = os.path.join(tmpdir, "figure.pdf") 

138 

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 

147 

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)