##// END OF EJS Templates
Merge pull request #4920 from ellisonbg/pdf-formatter...
Brian E. Granger -
r15212:e68e13c3 merge
parent child Browse files
Show More
@@ -24,7 +24,7 b' import struct'
24
24
25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 unicode_type)
26 unicode_type)
27
27 from IPython.testing.skipdoctest import skip_doctest
28 from .displaypub import publish_display_data
28 from .displaypub import publish_display_data
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
@@ -271,6 +271,24 b' def display_javascript(*objs, **kwargs):'
271 """
271 """
272 _display_mimetype('application/javascript', objs, **kwargs)
272 _display_mimetype('application/javascript', objs, **kwargs)
273
273
274
275 def display_pdf(*objs, **kwargs):
276 """Display the PDF representation of an object.
277
278 Parameters
279 ----------
280 objs : tuple of objects
281 The Python objects to display, or if raw=True raw javascript data to
282 display.
283 raw : bool
284 Are the data objects raw data or Python objects that need to be
285 formatted before display? [default: False]
286 metadata : dict (optional)
287 Metadata to be associated with the specific mimetype output.
288 """
289 _display_mimetype('application/pdf', objs, **kwargs)
290
291
274 #-----------------------------------------------------------------------------
292 #-----------------------------------------------------------------------------
275 # Smart classes
293 # Smart classes
276 #-----------------------------------------------------------------------------
294 #-----------------------------------------------------------------------------
@@ -699,3 +717,56 b' def clear_output(wait=False):'
699 io.stdout.flush()
717 io.stdout.flush()
700 print('\033[2K\r', file=io.stderr, end='')
718 print('\033[2K\r', file=io.stderr, end='')
701 io.stderr.flush()
719 io.stderr.flush()
720
721
722 @skip_doctest
723 def set_matplotlib_formats(*formats, **kwargs):
724 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
725
726 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
727
728 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
729
730 To set this in your config files use the following::
731
732 c.InlineBackend.figure_formats = {'pdf', 'png', 'svg'}
733 c.InlineBackend.quality = 90
734
735 Parameters
736 ----------
737 *formats : list, tuple
738 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
739 quality : int
740 A percentage for the quality of JPEG figures. Defaults to 90.
741 """
742 from IPython.core.interactiveshell import InteractiveShell
743 from IPython.core.pylabtools import select_figure_formats
744 shell = InteractiveShell.instance()
745 select_figure_formats(shell, formats, quality=90)
746
747 @skip_doctest
748 def set_matplotlib_close(close):
749 """Set whether the inline backend closes all figures automatically or not.
750
751 By default, the inline backend used in the IPython Notebook will close all
752 matplotlib figures automatically after each cell is run. This means that
753 plots in different cells won't interfere. Sometimes, you may want to make
754 a plot in one cell and then refine it in later cells. This can be accomplished
755 by::
756
757 In [1]: set_matplotlib_close(False)
758
759 To set this in your config files use the following::
760
761 c.InlineBackend.close_figures = False
762
763 Parameters
764 ----------
765 close : bool
766 Should all matplotlib figures be automatically closed after each cell is
767 run?
768 """
769 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
770 ilbe = InlineBackend.instance()
771 ilbe.close_figures = close
772
@@ -93,6 +93,7 b' class DisplayFormatter(Configurable):'
93 HTMLFormatter,
93 HTMLFormatter,
94 SVGFormatter,
94 SVGFormatter,
95 PNGFormatter,
95 PNGFormatter,
96 PDFFormatter,
96 JPEGFormatter,
97 JPEGFormatter,
97 LatexFormatter,
98 LatexFormatter,
98 JSONFormatter,
99 JSONFormatter,
@@ -116,6 +117,7 b' class DisplayFormatter(Configurable):'
116 * text/latex
117 * text/latex
117 * application/json
118 * application/json
118 * application/javascript
119 * application/javascript
120 * application/pdf
119 * image/png
121 * image/png
120 * image/jpeg
122 * image/jpeg
121 * image/svg+xml
123 * image/svg+xml
@@ -766,11 +768,29 b' class JavascriptFormatter(BaseFormatter):'
766
768
767 print_method = ObjectName('_repr_javascript_')
769 print_method = ObjectName('_repr_javascript_')
768
770
771
772 class PDFFormatter(BaseFormatter):
773 """A PDF formatter.
774
775 To defined the callables that compute to PDF representation of your
776 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
777 or :meth:`for_type_by_name` methods to register functions that handle
778 this.
779
780 The return value of this formatter should be raw PDF data, *not*
781 base64 encoded.
782 """
783 format_type = Unicode('application/pdf')
784
785 print_method = ObjectName('_repr_pdf_')
786
787
769 FormatterABC.register(BaseFormatter)
788 FormatterABC.register(BaseFormatter)
770 FormatterABC.register(PlainTextFormatter)
789 FormatterABC.register(PlainTextFormatter)
771 FormatterABC.register(HTMLFormatter)
790 FormatterABC.register(HTMLFormatter)
772 FormatterABC.register(SVGFormatter)
791 FormatterABC.register(SVGFormatter)
773 FormatterABC.register(PNGFormatter)
792 FormatterABC.register(PNGFormatter)
793 FormatterABC.register(PDFFormatter)
774 FormatterABC.register(JPEGFormatter)
794 FormatterABC.register(JPEGFormatter)
775 FormatterABC.register(LatexFormatter)
795 FormatterABC.register(LatexFormatter)
776 FormatterABC.register(JSONFormatter)
796 FormatterABC.register(JSONFormatter)
@@ -789,6 +809,7 b' def format_display_data(obj, include=None, exclude=None):'
789 * text/latex
809 * text/latex
790 * application/json
810 * application/json
791 * application/javascript
811 * application/javascript
812 * application/pdf
792 * image/png
813 * image/png
793 * image/jpeg
814 * image/jpeg
794 * image/svg+xml
815 * image/svg+xml
@@ -47,28 +47,32 b' class PylabMagics(Magics):'
47 """Set up matplotlib to work interactively.
47 """Set up matplotlib to work interactively.
48
48
49 This function lets you activate matplotlib interactive support
49 This function lets you activate matplotlib interactive support
50 at any point during an IPython session.
50 at any point during an IPython session. It does not import anything
51 It does not import anything into the interactive namespace.
51 into the interactive namespace.
52
52
53 If you are using the inline matplotlib backend for embedded figures,
53 If you are using the inline matplotlib backend in the IPython Notebook
54 you can adjust its behavior via the %config magic::
54 you can set which figure formats are enabled using the following::
55
55
56 # enable SVG figures, necessary for SVG+XHTML export in the qtconsole
56 In [1]: from IPython.display import set_matplotlib_formats
57 In [1]: %config InlineBackend.figure_format = 'svg'
58
57
59 # change the behavior of closing all figures at the end of each
58 In [2]: set_matplotlib_formats('pdf', 'svg')
60 # execution (cell), or allowing reuse of active figures across
59
61 # cells:
60 See the docstring of `IPython.display.set_matplotlib_formats` and
62 In [2]: %config InlineBackend.close_figures = False
61 `IPython.display.set_matplotlib_close` for more information on
62 changing the behavior of the inline backend.
63
63
64 Examples
64 Examples
65 --------
65 --------
66 In this case, where the MPL default is TkAgg::
66 To enable the inline backend for usage with the IPython Notebook::
67
68 In [1]: %matplotlib inline
69
70 In this case, where the matplotlib default is TkAgg::
67
71
68 In [2]: %matplotlib
72 In [2]: %matplotlib
69 Using matplotlib backend: TkAgg
73 Using matplotlib backend: TkAgg
70
74
71 But you can explicitly request a different backend::
75 But you can explicitly request a different GUI backend::
72
76
73 In [3]: %matplotlib qt
77 In [3]: %matplotlib qt
74 """
78 """
@@ -25,6 +25,7 b' from io import BytesIO'
25
25
26 from IPython.core.display import _pngxy
26 from IPython.core.display import _pngxy
27 from IPython.utils.decorators import flag_calls
27 from IPython.utils.decorators import flag_calls
28 from IPython.utils import py3compat
28
29
29 # If user specifies a GUI, that dictates the backend, otherwise we read the
30 # If user specifies a GUI, that dictates the backend, otherwise we read the
30 # user's mpl default from the mpl rc structure
31 # user's mpl default from the mpl rc structure
@@ -165,10 +166,17 b' def mpl_runner(safe_execfile):'
165 return mpl_execfile
166 return mpl_execfile
166
167
167
168
168 def select_figure_format(shell, fmt, quality=90):
169 def select_figure_formats(shell, formats, quality=90):
169 """Select figure format for inline backend, can be 'png', 'retina', 'jpg', or 'svg'.
170 """Select figure formats for the inline backend.
170
171
171 Using this method ensures only one figure format is active at a time.
172 Parameters
173 ==========
174 shell : InteractiveShell
175 The main IPython instance.
176 formats : list
177 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
178 quality : int
179 A percentage for the quality of JPEG figures.
172 """
180 """
173 from matplotlib.figure import Figure
181 from matplotlib.figure import Figure
174 from IPython.kernel.zmq.pylab import backend_inline
182 from IPython.kernel.zmq.pylab import backend_inline
@@ -176,9 +184,14 b' def select_figure_format(shell, fmt, quality=90):'
176 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
184 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
177 png_formatter = shell.display_formatter.formatters['image/png']
185 png_formatter = shell.display_formatter.formatters['image/png']
178 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
186 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
187 pdf_formatter = shell.display_formatter.formatters['application/pdf']
188
189 if isinstance(formats, py3compat.string_types):
190 formats = {formats}
179
191
180 [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ]
192 [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ]
181
193
194 for fmt in formats:
182 if fmt == 'png':
195 if fmt == 'png':
183 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
196 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
184 elif fmt in ('png2x', 'retina'):
197 elif fmt in ('png2x', 'retina'):
@@ -187,11 +200,10 b' def select_figure_format(shell, fmt, quality=90):'
187 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality))
200 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality))
188 elif fmt == 'svg':
201 elif fmt == 'svg':
189 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
202 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
203 elif fmt == 'pdf':
204 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf'))
190 else:
205 else:
191 raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', not %r" % fmt)
206 raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', 'pdf' not %r" % fmt)
192
193 # set the format to be used in the backend()
194 backend_inline._figure_format = fmt
195
207
196 #-----------------------------------------------------------------------------
208 #-----------------------------------------------------------------------------
197 # Code for initializing matplotlib and importing pylab
209 # Code for initializing matplotlib and importing pylab
@@ -342,5 +354,5 b' def configure_inline_support(shell, backend):'
342 del shell._saved_rcParams
354 del shell._saved_rcParams
343
355
344 # Setup the default figure format
356 # Setup the default figure format
345 select_figure_format(shell, cfg.figure_format, cfg.quality)
357 select_figure_formats(shell, cfg.figure_formats, cfg.quality)
346
358
@@ -8,7 +8,9 b' except:'
8 numpy = None
8 numpy = None
9 import nose.tools as nt
9 import nose.tools as nt
10
10
11 from IPython.core.formatters import PlainTextFormatter, HTMLFormatter, _mod_name_key
11 from IPython.core.formatters import (
12 PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key
13 )
12 from IPython.utils.io import capture_output
14 from IPython.utils.io import capture_output
13
15
14 class A(object):
16 class A(object):
@@ -279,4 +281,11 b' def test_warn_error_pretty_method():'
279 nt.assert_in("text/plain", captured.stderr)
281 nt.assert_in("text/plain", captured.stderr)
280 nt.assert_in("argument", captured.stderr)
282 nt.assert_in("argument", captured.stderr)
281
283
284 class MakePDF(object):
285 def _repr_pdf_(self):
286 return 'PDF'
282
287
288 def test_pdf_formatter():
289 pdf = MakePDF()
290 f = PDFFormatter()
291 nt.assert_equal(f(pdf), 'PDF')
@@ -252,6 +252,7 b' var IPython = (function (IPython) {'
252 'image/svg+xml',
252 'image/svg+xml',
253 'image/png',
253 'image/png',
254 'image/jpeg',
254 'image/jpeg',
255 'application/pdf',
255 'text/plain'
256 'text/plain'
256 ];
257 ];
257
258
@@ -620,6 +621,17 b' var IPython = (function (IPython) {'
620 };
621 };
621
622
622
623
624 OutputArea.prototype.append_pdf = function (pdf, md, element) {
625 var type = 'application/pdf';
626 var toinsert = this.create_output_subarea(md, "output_pdf", type);
627 var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
628 a.attr('target', '_blank');
629 a.text('View PDF')
630 toinsert.append(a);
631 element.append(toinsert);
632 return toinsert;
633 }
634
623 OutputArea.prototype.append_latex = function (latex, md, element) {
635 OutputArea.prototype.append_latex = function (latex, md, element) {
624 // This method cannot do the typesetting because the latex first has to
636 // This method cannot do the typesetting because the latex first has to
625 // be on the page.
637 // be on the page.
@@ -807,6 +819,7 b' var IPython = (function (IPython) {'
807 "image/svg+xml" : "svg",
819 "image/svg+xml" : "svg",
808 "image/png" : "png",
820 "image/png" : "png",
809 "image/jpeg" : "jpeg",
821 "image/jpeg" : "jpeg",
822 "application/pdf" : "pdf",
810 "text/latex" : "latex",
823 "text/latex" : "latex",
811 "application/json" : "json",
824 "application/json" : "json",
812 "application/javascript" : "javascript",
825 "application/javascript" : "javascript",
@@ -818,6 +831,7 b' var IPython = (function (IPython) {'
818 "svg" : "image/svg+xml",
831 "svg" : "image/svg+xml",
819 "png" : "image/png",
832 "png" : "image/png",
820 "jpeg" : "image/jpeg",
833 "jpeg" : "image/jpeg",
834 "pdf" : "application/pdf",
821 "latex" : "text/latex",
835 "latex" : "text/latex",
822 "json" : "application/json",
836 "json" : "application/json",
823 "javascript" : "application/javascript",
837 "javascript" : "application/javascript",
@@ -830,6 +844,7 b' var IPython = (function (IPython) {'
830 'image/svg+xml',
844 'image/svg+xml',
831 'image/png',
845 'image/png',
832 'image/jpeg',
846 'image/jpeg',
847 'application/pdf',
833 'text/plain'
848 'text/plain'
834 ];
849 ];
835
850
@@ -842,6 +857,7 b' var IPython = (function (IPython) {'
842 "text/latex" : OutputArea.prototype.append_latex,
857 "text/latex" : OutputArea.prototype.append_latex,
843 "application/json" : OutputArea.prototype.append_json,
858 "application/json" : OutputArea.prototype.append_json,
844 "application/javascript" : OutputArea.prototype.append_javascript,
859 "application/javascript" : OutputArea.prototype.append_javascript,
860 "application/pdf" : OutputArea.prototype.append_pdf
845 };
861 };
846
862
847 IPython.OutputArea = OutputArea;
863 IPython.OutputArea = OutputArea;
@@ -14,7 +14,9 b' This module does not import anything from matplotlib.'
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from IPython.config.configurable import SingletonConfigurable
16 from IPython.config.configurable import SingletonConfigurable
17 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool, Int, TraitError
17 from IPython.utils.traitlets import (
18 Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode
19 )
18 from IPython.utils.warn import warn
20 from IPython.utils.warn import warn
19
21
20 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
@@ -63,21 +65,26 b' class InlineBackend(InlineBackendConfig):'
63 inline backend."""
65 inline backend."""
64 )
66 )
65
67
68 figure_formats = Set({'png'}, config=True,
69 help="""A set of figure formats to enable: 'png',
70 'retina', 'jpeg', 'svg', 'pdf'.""")
66
71
67 figure_format = CaselessStrEnum(['svg', 'png', 'retina', 'jpg'],
72 def _figure_formats_changed(self, name, old, new):
68 default_value='png', config=True,
73 from IPython.core.pylabtools import select_figure_formats
69 help="""The image format for figures with the inline
74 if 'jpg' in new or 'jpeg' in new:
70 backend. JPEG requires the PIL/Pillow library.""")
71
72 def _figure_format_changed(self, name, old, new):
73 from IPython.core.pylabtools import select_figure_format
74 if new in {"jpg", "jpeg"}:
75 if not pil_available():
75 if not pil_available():
76 raise TraitError("Requires PIL/Pillow for JPG figures")
76 raise TraitError("Requires PIL/Pillow for JPG figures")
77 if self.shell is None:
77 if self.shell is None:
78 return
78 return
79 else:
79 else:
80 select_figure_format(self.shell, new)
80 select_figure_formats(self.shell, new)
81
82 figure_format = Unicode(config=True, help="""The figure format to enable (deprecated
83 use `figure_formats` instead)""")
84
85 def _figure_format_changed(self, name, old, new):
86 if new:
87 self.figure_formats = {new}
81
88
82 quality = Int(default_value=90, config=True,
89 quality = Int(default_value=90, config=True,
83 help="Quality of compression [10-100], currently for lossy JPEG only.")
90 help="Quality of compression [10-100], currently for lossy JPEG only.")
@@ -119,6 +119,8 b" PNG64 = b'iVBORw0KG'"
119 JPEG = b'\xff\xd8'
119 JPEG = b'\xff\xd8'
120 # front of JPEG base64-encoded
120 # front of JPEG base64-encoded
121 JPEG64 = b'/9'
121 JPEG64 = b'/9'
122 # front of PDF base64-encoded
123 PDF64 = b'JVBER'
122
124
123 def encode_images(format_dict):
125 def encode_images(format_dict):
124 """b64-encodes images in a displaypub format dict
126 """b64-encodes images in a displaypub format dict
@@ -136,7 +138,7 b' def encode_images(format_dict):'
136
138
137 format_dict : dict
139 format_dict : dict
138 A copy of the same dictionary,
140 A copy of the same dictionary,
139 but binary image data ('image/png' or 'image/jpeg')
141 but binary image data ('image/png', 'image/jpeg' or 'application/pdf')
140 is base64-encoded.
142 is base64-encoded.
141
143
142 """
144 """
@@ -156,6 +158,13 b' def encode_images(format_dict):'
156 jpegdata = encodebytes(jpegdata)
158 jpegdata = encodebytes(jpegdata)
157 encoded['image/jpeg'] = jpegdata.decode('ascii')
159 encoded['image/jpeg'] = jpegdata.decode('ascii')
158
160
161 pdfdata = format_dict.get('application/pdf')
162 if isinstance(pdfdata, bytes):
163 # make sure we don't double-encode
164 if not pdfdata.startswith(PDF64):
165 pdfdata = encodebytes(pdfdata)
166 encoded['application/pdf'] = pdfdata.decode('ascii')
167
159 return encoded
168 return encoded
160
169
161
170
@@ -69,10 +69,12 b' def test_encode_images():'
69 # invalid data, but the header and footer are from real files
69 # invalid data, but the header and footer are from real files
70 pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
70 pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
71 jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
71 jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
72 pdfdata = b'%PDF-1.\ntrailer<</Root<</Pages<</Kids[<</MediaBox[0 0 3 3]>>]>>>>>>'
72
73
73 fmt = {
74 fmt = {
74 'image/png' : pngdata,
75 'image/png' : pngdata,
75 'image/jpeg' : jpegdata,
76 'image/jpeg' : jpegdata,
77 'application/pdf' : pdfdata
76 }
78 }
77 encoded = encode_images(fmt)
79 encoded = encode_images(fmt)
78 for key, value in iteritems(fmt):
80 for key, value in iteritems(fmt):
General Comments 0
You need to be logged in to leave comments. Login now