svg2pdf.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. """Module containing a preprocessor that converts outputs in the notebook from
  2. one format to another.
  3. """
  4. # Copyright (c) Jupyter Development Team.
  5. # Distributed under the terms of the Modified BSD License.
  6. import base64
  7. import io
  8. import os
  9. import sys
  10. import subprocess
  11. from ipython_genutils.py3compat import cast_unicode_py2
  12. from testpath.tempdir import TemporaryDirectory
  13. from traitlets import Unicode, default
  14. from .convertfigures import ConvertFiguresPreprocessor
  15. if sys.version_info >= (3,3):
  16. from shutil import which
  17. get_inkscape_path = which('inkscape')
  18. else:
  19. get_inkscape_path = None
  20. INKSCAPE_APP = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
  21. if sys.platform == "win32":
  22. try:
  23. import winreg
  24. except ImportError:
  25. import _winreg as winreg
  26. class SVG2PDFPreprocessor(ConvertFiguresPreprocessor):
  27. """
  28. Converts all of the outputs in a notebook from SVG to PDF.
  29. """
  30. @default('from_format')
  31. def _from_format_default(self):
  32. return 'image/svg+xml'
  33. @default('to_format')
  34. def _to_format_default(self):
  35. return 'application/pdf'
  36. command = Unicode(
  37. help="""The command to use for converting SVG to PDF
  38. This string is a template, which will be formatted with the keys
  39. to_filename and from_filename.
  40. The conversion call must read the SVG from {from_filename},
  41. and write a PDF to {to_filename}.
  42. """).tag(config=True)
  43. @default('command')
  44. def _command_default(self):
  45. return self.inkscape + \
  46. ' --without-gui --export-pdf="{to_filename}" "{from_filename}"'
  47. inkscape = Unicode(help="The path to Inkscape, if necessary").tag(config=True)
  48. @default('inkscape')
  49. def _inkscape_default(self):
  50. if get_inkscape_path is not None:
  51. return get_inkscape_path
  52. if sys.platform == "darwin":
  53. if os.path.isfile(INKSCAPE_APP):
  54. return INKSCAPE_APP
  55. if sys.platform == "win32":
  56. wr_handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
  57. try:
  58. rkey = winreg.OpenKey(wr_handle, "SOFTWARE\\Classes\\inkscape.svg\\DefaultIcon")
  59. inkscape = winreg.QueryValueEx(rkey, "")[0]
  60. except FileNotFoundError:
  61. raise FileNotFoundError("Inkscape executable not found")
  62. return inkscape
  63. return "inkscape"
  64. def convert_figure(self, data_format, data):
  65. """
  66. Convert a single SVG figure to PDF. Returns converted data.
  67. """
  68. #Work in a temporary directory
  69. with TemporaryDirectory() as tmpdir:
  70. #Write fig to temp file
  71. input_filename = os.path.join(tmpdir, 'figure.svg')
  72. # SVG data is unicode text
  73. with io.open(input_filename, 'w', encoding='utf8') as f:
  74. f.write(cast_unicode_py2(data))
  75. #Call conversion application
  76. output_filename = os.path.join(tmpdir, 'figure.pdf')
  77. shell = self.command.format(from_filename=input_filename,
  78. to_filename=output_filename)
  79. subprocess.call(shell, shell=True) #Shell=True okay since input is trusted.
  80. #Read output from drive
  81. # return value expects a filename
  82. if os.path.isfile(output_filename):
  83. with open(output_filename, 'rb') as f:
  84. # PDF is a nb supported binary, data type, so base64 encode.
  85. return base64.encodestring(f.read())
  86. else:
  87. raise TypeError("Inkscape svg to pdf conversion failed")