(self, orig, dest)
| 146 | |
| 147 | class _SVGConverter(_Converter): |
| 148 | def __call__(self, orig, dest): |
| 149 | old_inkscape = mpl._get_executable_info("inkscape").version.major < 1 |
| 150 | terminator = b"\n>" if old_inkscape else b"> " |
| 151 | if not hasattr(self, "_tmpdir"): |
| 152 | self._tmpdir = TemporaryDirectory() |
| 153 | # On Windows, we must make sure that self._proc has terminated |
| 154 | # (which __del__ does) before clearing _tmpdir. |
| 155 | weakref.finalize(self._tmpdir, self.__del__) |
| 156 | if (not self._proc # First run. |
| 157 | or self._proc.poll() is not None): # Inkscape terminated. |
| 158 | if self._proc is not None and self._proc.poll() is not None: |
| 159 | for stream in filter(None, [self._proc.stdin, |
| 160 | self._proc.stdout, |
| 161 | self._proc.stderr]): |
| 162 | stream.close() |
| 163 | env = { |
| 164 | **os.environ, |
| 165 | # If one passes e.g. a png file to Inkscape, it will try to |
| 166 | # query the user for conversion options via a GUI (even with |
| 167 | # `--without-gui`). Unsetting `DISPLAY` prevents this (and |
| 168 | # causes GTK to crash and Inkscape to terminate, but that'll |
| 169 | # just be reported as a regular exception below). |
| 170 | "DISPLAY": "", |
| 171 | # Do not load any user options. |
| 172 | "INKSCAPE_PROFILE_DIR": self._tmpdir.name, |
| 173 | } |
| 174 | # Old versions of Inkscape (e.g. 0.48.3.1) seem to sometimes |
| 175 | # deadlock when stderr is redirected to a pipe, so we redirect it |
| 176 | # to a temporary file instead. This is not necessary anymore as of |
| 177 | # Inkscape 0.92.1. |
| 178 | stderr = TemporaryFile() |
| 179 | self._proc = subprocess.Popen( |
| 180 | ["inkscape", "--without-gui", "--shell"] if old_inkscape else |
| 181 | ["inkscape", "--shell"], |
| 182 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr, |
| 183 | env=env, cwd=self._tmpdir.name) |
| 184 | # Slight abuse, but makes shutdown handling easier. |
| 185 | self._proc.stderr = stderr |
| 186 | try: |
| 187 | self._read_until(terminator) |
| 188 | except _ConverterError as err: |
| 189 | raise OSError( |
| 190 | "Failed to start Inkscape in interactive mode:\n\n" |
| 191 | + err.args[0]) from err |
| 192 | |
| 193 | # Inkscape's shell mode does not support escaping metacharacters in the |
| 194 | # filename ("\n", and ":;" for inkscape>=1). Avoid any problems by |
| 195 | # running from a temporary directory and using fixed filenames. |
| 196 | inkscape_orig = Path(self._tmpdir.name, os.fsdecode(b"f.svg")) |
| 197 | inkscape_dest = Path(self._tmpdir.name, os.fsdecode(b"f.png")) |
| 198 | try: |
| 199 | inkscape_orig.symlink_to(Path(orig).resolve()) |
| 200 | except OSError: |
| 201 | shutil.copyfile(orig, inkscape_orig) |
| 202 | self._proc.stdin.write( |
| 203 | b"f.svg --export-png=f.png\n" if old_inkscape else |
| 204 | b"file-open:f.svg;export-filename:f.png;export-do;file-close\n") |
| 205 | self._proc.stdin.flush() |
nothing calls this directly
no test coverage detected