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

nbconvert 5.6.1 failed with message: "nbconvert failed: Inkscape svg to pdf conversion failed" in Win 10 #1325

Open
theoky opened this issue Jul 15, 2020 · 15 comments
Labels
bug good first issue great for new contributors help wanted
Milestone

Comments

@theoky
Copy link

theoky commented Jul 15, 2020

This happens when trying to convert a notebook with svg graphics embedded.

I have a hunch that the svg2pdf.py code can not handle a blank in the program name of inkscape as it is the case on my machine ("C:\Program Files\Inkscape\bin\inkscape.exe"). Also the text before the exception "'C:\Program' is not recognized as an internal or external command, operable program or batch file." gives this impression.

Environment

  • Windows 10 English
  • Inkscape 1.0

Steps to reproduce

  • generate output with SVG graphics (e.g. with display(SVG(filename='test.svg'))
  • try to export it to PDF (download as pdf via latex)

(Rather) minimal notebook: Gantt_Minimal_SVG.txt

Nbconvert version: 5.6.1

@MSeal
Copy link
Contributor

MSeal commented Jul 16, 2020

The alpha for nbconvert -- 6.0.0a4 -- should fix the issue with inkscape 1.0 on windows, if you want to give that a try.

@theoky
Copy link
Author

theoky commented Jul 16, 2020

Thanks. I've upgraded an anaconda env to nbconvert 6.0.0.a4. After that I've got this error message when trying to export to pdf:

[E 08:02:48.932 NotebookApp] nbconvert failed: [WinError 2] The system cannot find the file specified
    Traceback (most recent call last):
      File "...anaconda env...\lib\site-packages\traitlets\traitlets.py", line 528, in get
        value = obj._trait_values[self.name]
    KeyError: 'command'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "...anaconda env...\lib\site-packages\traitlets\traitlets.py", line 528, in get
        value = obj._trait_values[self.name]
    KeyError: 'inkscape_version'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "...anaconda env...\lib\site-packages\notebook\nbconvert\handlers.py", line 132, in get
        resources=resource_dict
      File "...anaconda env...\lib\site-packages\nbconvert\exporters\pdf.py", line 176, in from_notebook_node
        nb, resources=resources, **kw
      File "...anaconda env...\lib\site-packages\nbconvert\exporters\latex.py", line 77, in from_notebook_node
        return super().from_notebook_node(nb, resources, **kw)
      File "...anaconda env...\lib\site-packages\nbconvert\exporters\templateexporter.py", line 340, in from_notebook_node
        nb_copy, resources = super().from_notebook_node(nb, resources, **kw)
      File "...anaconda env...\lib\site-packages\nbconvert\exporters\exporter.py", line 143, in from_notebook_node
        nb_copy, resources = self._preprocess(nb_copy, resources)
      File "...anaconda env...\lib\site-packages\nbconvert\exporters\exporter.py", line 320, in _preprocess
        nbc, resc = preprocessor(nbc, resc)
      File "...anaconda env...\lib\site-packages\nbconvert\preprocessors\base.py", line 47, in __call__
        return self.preprocess(nb, resources)
      File "...anaconda env...\lib\site-packages\nbconvert\preprocessors\base.py", line 69, in preprocess
        nb.cells[index], resources = self.preprocess_cell(cell, resources, index)
      File "...anaconda env...\lib\site-packages\nbconvert\preprocessors\convertfigures.py", line 46, in preprocess_cell
        self.from_format, output.data[self.from_format])
      File "...anaconda env...\lib\site-packages\nbconvert\preprocessors\svg2pdf.py", line 126, in convert_figure
        shell = self.command.format(from_filename=input_filename,
      File "...anaconda env...\lib\site-packages\traitlets\traitlets.py", line 556, in __get__
        return self.get(obj, cls)
      File "...anaconda env...\lib\site-packages\traitlets\traitlets.py", line 535, in get
        value = self._validate(obj, dynamic_default())
      File "...anaconda env...\lib\site-packages\nbconvert\preprocessors\svg2pdf.py", line 78, in _command_default
        major_verison = self.inkscape_version.split('.')[0]
      File "...anaconda env...\lib\site-packages\traitlets\traitlets.py", line 556, in __get__
        return self.get(obj, cls)
      File "...anaconda env...\lib\site-packages\traitlets\traitlets.py", line 535, in get
        value = self._validate(obj, dynamic_default())
      File "...anaconda env...\lib\site-packages\nbconvert\preprocessors\svg2pdf.py", line 60, in _inkscape_version_default
        stderr=subprocess.PIPE)
      File "...anaconda env...\lib\subprocess.py", line 800, in __init__
        restore_signals, start_new_session)
      File "...anaconda env...\lib\subprocess.py", line 1207, in _execute_child
        startupinfo)
    FileNotFoundError: [WinError 2] The system cannot find the file specified
[W 08:02:48.939 NotebookApp] 500 GET /nbconvert/pdf/Gantt_Spielereien/Gantt_Minimal_SVG.ipynb?download=true (::1): nbconvert failed: [WinError 2] The system cannot find the file specified

btw, exporting to pdf without SVG works in this environment.

@theoky
Copy link
Author

theoky commented Jul 23, 2020

I did a little research with the svg2pdf.py of the above mentioned alpha and found the following:

My registry contains the string value "C:\Program Files\Inkscape\bin\inkscape.exe,0" (without quotation marks). When you enter that text in a command line prompt, the trailing ",0" leads to

'"C:\Program Files\Inkscape\bin\inkscape.exe,0"' is not recognized as an internal or external command, operable program or batch file."

Second, the blank in "Program Files" causes problems.

If I modify svg2pdf.py to remove the ",0" when getting the string from the registry, e.g., by

inkscape = winreg.QueryValueEx(rkey, "")[0].split(',')[0]

and change the command string from

      return '{inkscape}{gui_option}{export_option}='.format(
            inkscape=self.inkscape, export_option=export_option, gui_option=gui_option
        ) + '"{to_filename}" "{from_filename}"'

to

      return '"{inkscape}"{gui_option}{export_option}='.format(
            inkscape=self.inkscape, export_option=export_option, gui_option=gui_option
        ) + '"{to_filename}" "{from_filename}"'

, then svg conversion with inkscape works for me.

As I don't know if these changes will work for all OS (and not only for my Win10), I'll leave it here for someone to pick it up, include it, and test it properly.

@zomborid
Copy link

zomborid commented Jul 24, 2020

@MSeal theoky was quicker. :)
I had the exact same problem on Win10 and 6.0.0.a4, with the message: nbconvert failed: Inkscape svg to pdf conversion failed
The server showed an exception, something like: 'C:\Program' is not recognized as a command.
Quick solution for beginners: reinstall Inkscape in a path with no whitespace.

@MSeal
Copy link
Contributor

MSeal commented Jul 24, 2020

Ahh thanks for finding the existing issue 👍

I've been swamped and my free time has been limited so I'm not sure when I'll get to this. Would love to have a new contributor try to put this into a PR with a test improvement.

@leanhdung1994
Copy link

I did a little research with the svg2pdf.py of the above mentioned alpha and found the following:

My registry contains the string value "C:\Program Files\Inkscape\bin\inkscape.exe,0" (without quotation marks). When you enter that text in a command line prompt, the trailing ",0" leads to

'"C:\Program Files\Inkscape\bin\inkscape.exe,0"' is not recognized as an internal or external command, operable program or batch file."

Second, the blank in "Program Files" causes problems.

If I modify svg2pdf.py to remove the ",0" when getting the string from the registry, e.g., by

inkscape = winreg.QueryValueEx(rkey, "")[0].split(',')[0]

and change the command string from

      return '{inkscape}{gui_option}{export_option}='.format(
            inkscape=self.inkscape, export_option=export_option, gui_option=gui_option
        ) + '"{to_filename}" "{from_filename}"'

to

      return '"{inkscape}"{gui_option}{export_option}='.format(
            inkscape=self.inkscape, export_option=export_option, gui_option=gui_option
        ) + '"{to_filename}" "{from_filename}"'

, then svg conversion with inkscape works for me.

As I don't know if these changes will work for all OS (and not only for my Win10), I'll leave it here for someone to pick it up, include it, and test it properly.

Hi @theoky, I'm trying to follow your solution. Could you please explain how to locate the file svg2pdf.py?

@theoky
Copy link
Author

theoky commented Oct 18, 2020

Hi @theoky, I'm trying to follow your solution. Could you please explain how to locate the file svg2pdf.py?

I can find it on my machine here:

  • <path to winpython installation>\python-3.#.#.amd64\Lib\site-packages\nbconvert\preprocessors
  • <path to where your anaconda environments are>\<environment name>\Lib\site-packages\nbconvert\preprocessors

Hope that helps.

@leanhdung1994
Copy link

Hi @theoky, I'm trying to follow your solution. Could you please explain how to locate the file svg2pdf.py?

I can find it on my machine here:

  • \python-3.#.#.amd64\Lib\site-packages\nbconvert\preprocessors
  • <environment name>\Lib\site-packages\nbconvert\preprocessors

Hope that helps.

Thank you so much. I've just found it. It's sad that the content of the file is changed and I'm unable to apply your method.

"""Module containing a preprocessor that converts outputs in the notebook from 
one format to another.
"""

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

import base64
import io
import os
import sys
import subprocess

from ipython_genutils.py3compat import cast_unicode_py2
from testpath.tempdir import TemporaryDirectory
from traitlets import Unicode, default

from .convertfigures import ConvertFiguresPreprocessor

if sys.version_info >= (3,3):
    from shutil import which
    get_inkscape_path = which('inkscape')
else:
    get_inkscape_path = None


INKSCAPE_APP = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'

if sys.platform == "win32":
    try:
        import winreg
    except ImportError:
        import _winreg as winreg


class SVG2PDFPreprocessor(ConvertFiguresPreprocessor):
    """
    Converts all of the outputs in a notebook from SVG to PDF.
    """

    @default('from_format')
    def _from_format_default(self):
        return 'image/svg+xml'

    @default('to_format')
    def _to_format_default(self):
        return 'application/pdf'

    command = Unicode(
        help="""The command to use for converting SVG to PDF
        
        This string is a template, which will be formatted with the keys
        to_filename and from_filename.
        
        The conversion call must read the SVG from {from_filename},
        and write a PDF to {to_filename}.
        """).tag(config=True)

    @default('command')
    def _command_default(self):
        return self.inkscape + \
               ' --without-gui --export-pdf="{to_filename}" "{from_filename}"'
    
    inkscape = Unicode(help="The path to Inkscape, if necessary").tag(config=True)
    @default('inkscape')
    def _inkscape_default(self):
        if get_inkscape_path is not None:
            return get_inkscape_path 
        if sys.platform == "darwin":
            if os.path.isfile(INKSCAPE_APP):
                return INKSCAPE_APP
        if sys.platform == "win32":
            wr_handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
            try:
                rkey = winreg.OpenKey(wr_handle, "SOFTWARE\\Classes\\inkscape.svg\\DefaultIcon")
                inkscape = winreg.QueryValueEx(rkey, "")[0]
            except FileNotFoundError:
                raise FileNotFoundError("Inkscape executable not found")
            return inkscape
        return "inkscape"


    def convert_figure(self, data_format, data):
        """
        Convert a single SVG figure to PDF.  Returns converted data.
        """

        #Work in a temporary directory
        with TemporaryDirectory() as tmpdir:
            
            #Write fig to temp file
            input_filename = os.path.join(tmpdir, 'figure.svg')
            # SVG data is unicode text
            with io.open(input_filename, 'w', encoding='utf8') as f:
                f.write(cast_unicode_py2(data))

            #Call conversion application
            output_filename = os.path.join(tmpdir, 'figure.pdf')
            shell = self.command.format(from_filename=input_filename, 
                                   to_filename=output_filename)
            subprocess.call(shell, shell=True) #Shell=True okay since input is trusted.

            #Read output from drive
            # return value expects a filename
            if os.path.isfile(output_filename):
                with open(output_filename, 'rb') as f:
                    # PDF is a nb supported binary, data type, so base64 encode.
                    return base64.encodestring(f.read())
            else:
                raise TypeError("Inkscape svg to pdf conversion failed")

@MichaelAdolph
Copy link
Contributor

I stumbled on the same issued and found #244. I created the pull request #1469 for this.

@theoky
Copy link
Author

theoky commented Nov 20, 2020

I created the pull request #1469 for this.

Thanks for creating the pull request.

@sschuldenzucker
Copy link

Is this merged? Should this be closed? I'm still getting this error in nbconvert 6.1.0

@abafoon
Copy link

abafoon commented Nov 4, 2021

@MSeal theoky was quicker. :) I had the exact same problem on Win10 and 6.0.0.a4, with the message: nbconvert failed: Inkscape svg to pdf conversion failed The server showed an exception, something like: 'C:\Program' is not recognized as a command. Quick solution for beginners: reinstall Inkscape in a path with no whitespace.

That helped, finally!

@meeseeksmachine
Copy link

This issue has been mentioned on Jupyter Community Forum. There might be relevant details there:

https://discourse.jupyter.org/t/inkscape-error-when-converting-julia-notebook-to-pdf/13112/2

@theoky
Copy link
Author

theoky commented Oct 30, 2022

In the meantime I did an update to nbconvert 7.2.3 and installed inkscape 1.2.1 in a path without space.

While the command string handling changed, the code for getting the inkscape exe path on Windows in the file svg2pdf.py still is

inkscape = winreg.QueryValueEx(rkey, "")[0]

and still returns

C:\Programs\Inkscape\bin\inkscape.exe,0

on two Win 10 machines (one German, one English), thus aborting the nbconvert process with

File "...\lib\site-packages\nbconvert\preprocessors\svg2pdf.py", line 55, in _inkscape_version_default
    p = subprocess.Popen(
  File "...\lib\subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "...\lib\subprocess.py", line 1420, in _execute_child
    hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
FileNotFoundError: [WinError 2] Das System kann die angegebene Datei nicht finden

So I still need to patch this line it to

inkscape = winreg.QueryValueEx(rkey, "")[0].split(',')[0]

or some other code which removes the ",0" and then it works.

I don't know if this only happens on my Win 10 machines or on all...

@jpcbertoldo
Copy link

Is this merged? Should this be closed? I'm still getting this error in nbconvert 6.1.0

Re-post. Is it really solved?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug good first issue great for new contributors help wanted
Projects
None yet
Development

No branches or pull requests

9 participants