##// 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 25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 26 unicode_type)
27
27 from IPython.testing.skipdoctest import skip_doctest
28 28 from .displaypub import publish_display_data
29 29
30 30 #-----------------------------------------------------------------------------
@@ -271,6 +271,24 b' def display_javascript(*objs, **kwargs):'
271 271 """
272 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 293 # Smart classes
276 294 #-----------------------------------------------------------------------------
@@ -699,3 +717,56 b' def clear_output(wait=False):'
699 717 io.stdout.flush()
700 718 print('\033[2K\r', file=io.stderr, end='')
701 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 93 HTMLFormatter,
94 94 SVGFormatter,
95 95 PNGFormatter,
96 PDFFormatter,
96 97 JPEGFormatter,
97 98 LatexFormatter,
98 99 JSONFormatter,
@@ -116,6 +117,7 b' class DisplayFormatter(Configurable):'
116 117 * text/latex
117 118 * application/json
118 119 * application/javascript
120 * application/pdf
119 121 * image/png
120 122 * image/jpeg
121 123 * image/svg+xml
@@ -766,11 +768,29 b' class JavascriptFormatter(BaseFormatter):'
766 768
767 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 788 FormatterABC.register(BaseFormatter)
770 789 FormatterABC.register(PlainTextFormatter)
771 790 FormatterABC.register(HTMLFormatter)
772 791 FormatterABC.register(SVGFormatter)
773 792 FormatterABC.register(PNGFormatter)
793 FormatterABC.register(PDFFormatter)
774 794 FormatterABC.register(JPEGFormatter)
775 795 FormatterABC.register(LatexFormatter)
776 796 FormatterABC.register(JSONFormatter)
@@ -789,6 +809,7 b' def format_display_data(obj, include=None, exclude=None):'
789 809 * text/latex
790 810 * application/json
791 811 * application/javascript
812 * application/pdf
792 813 * image/png
793 814 * image/jpeg
794 815 * image/svg+xml
@@ -47,28 +47,32 b' class PylabMagics(Magics):'
47 47 """Set up matplotlib to work interactively.
48 48
49 49 This function lets you activate matplotlib interactive support
50 at any point during an IPython session.
51 It does not import anything into the interactive namespace.
50 at any point during an IPython session. It does not import anything
51 into the interactive namespace.
52 52
53 If you are using the inline matplotlib backend for embedded figures,
54 you can adjust its behavior via the %config magic::
55
56 # enable SVG figures, necessary for SVG+XHTML export in the qtconsole
57 In [1]: %config InlineBackend.figure_format = 'svg'
53 If you are using the inline matplotlib backend in the IPython Notebook
54 you can set which figure formats are enabled using the following::
55
56 In [1]: from IPython.display import set_matplotlib_formats
57
58 In [2]: set_matplotlib_formats('pdf', 'svg')
58 59
59 # change the behavior of closing all figures at the end of each
60 # execution (cell), or allowing reuse of active figures across
61 # cells:
62 In [2]: %config InlineBackend.close_figures = False
60 See the docstring of `IPython.display.set_matplotlib_formats` and
61 `IPython.display.set_matplotlib_close` for more information on
62 changing the behavior of the inline backend.
63 63
64 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 72 In [2]: %matplotlib
69 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 77 In [3]: %matplotlib qt
74 78 """
@@ -25,6 +25,7 b' from io import BytesIO'
25 25
26 26 from IPython.core.display import _pngxy
27 27 from IPython.utils.decorators import flag_calls
28 from IPython.utils import py3compat
28 29
29 30 # If user specifies a GUI, that dictates the backend, otherwise we read the
30 31 # user's mpl default from the mpl rc structure
@@ -165,10 +166,17 b' def mpl_runner(safe_execfile):'
165 166 return mpl_execfile
166 167
167 168
168 def select_figure_format(shell, fmt, quality=90):
169 """Select figure format for inline backend, can be 'png', 'retina', 'jpg', or 'svg'.
169 def select_figure_formats(shell, formats, quality=90):
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 181 from matplotlib.figure import Figure
174 182 from IPython.kernel.zmq.pylab import backend_inline
@@ -176,22 +184,26 b' def select_figure_format(shell, fmt, quality=90):'
176 184 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
177 185 png_formatter = shell.display_formatter.formatters['image/png']
178 186 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
187 pdf_formatter = shell.display_formatter.formatters['application/pdf']
179 188
180 [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ]
189 if isinstance(formats, py3compat.string_types):
190 formats = {formats}
181 191
182 if fmt == 'png':
183 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
184 elif fmt in ('png2x', 'retina'):
185 png_formatter.for_type(Figure, retina_figure)
186 elif fmt in ('jpg', 'jpeg'):
187 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality))
188 elif fmt == 'svg':
189 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
190 else:
191 raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', not %r" % fmt)
192 [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ]
192 193
193 # set the format to be used in the backend()
194 backend_inline._figure_format = fmt
194 for fmt in formats:
195 if fmt == 'png':
196 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
197 elif fmt in ('png2x', 'retina'):
198 png_formatter.for_type(Figure, retina_figure)
199 elif fmt in ('jpg', 'jpeg'):
200 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality))
201 elif fmt == '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'))
205 else:
206 raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', 'pdf' not %r" % fmt)
195 207
196 208 #-----------------------------------------------------------------------------
197 209 # Code for initializing matplotlib and importing pylab
@@ -342,5 +354,5 b' def configure_inline_support(shell, backend):'
342 354 del shell._saved_rcParams
343 355
344 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 8 numpy = None
9 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 14 from IPython.utils.io import capture_output
13 15
14 16 class A(object):
@@ -279,4 +281,11 b' def test_warn_error_pretty_method():'
279 281 nt.assert_in("text/plain", captured.stderr)
280 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 252 'image/svg+xml',
253 253 'image/png',
254 254 'image/jpeg',
255 'application/pdf',
255 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 635 OutputArea.prototype.append_latex = function (latex, md, element) {
624 636 // This method cannot do the typesetting because the latex first has to
625 637 // be on the page.
@@ -807,6 +819,7 b' var IPython = (function (IPython) {'
807 819 "image/svg+xml" : "svg",
808 820 "image/png" : "png",
809 821 "image/jpeg" : "jpeg",
822 "application/pdf" : "pdf",
810 823 "text/latex" : "latex",
811 824 "application/json" : "json",
812 825 "application/javascript" : "javascript",
@@ -818,6 +831,7 b' var IPython = (function (IPython) {'
818 831 "svg" : "image/svg+xml",
819 832 "png" : "image/png",
820 833 "jpeg" : "image/jpeg",
834 "pdf" : "application/pdf",
821 835 "latex" : "text/latex",
822 836 "json" : "application/json",
823 837 "javascript" : "application/javascript",
@@ -830,6 +844,7 b' var IPython = (function (IPython) {'
830 844 'image/svg+xml',
831 845 'image/png',
832 846 'image/jpeg',
847 'application/pdf',
833 848 'text/plain'
834 849 ];
835 850
@@ -842,6 +857,7 b' var IPython = (function (IPython) {'
842 857 "text/latex" : OutputArea.prototype.append_latex,
843 858 "application/json" : OutputArea.prototype.append_json,
844 859 "application/javascript" : OutputArea.prototype.append_javascript,
860 "application/pdf" : OutputArea.prototype.append_pdf
845 861 };
846 862
847 863 IPython.OutputArea = OutputArea;
@@ -14,7 +14,9 b' This module does not import anything from matplotlib.'
14 14 #-----------------------------------------------------------------------------
15 15
16 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 20 from IPython.utils.warn import warn
19 21
20 22 #-----------------------------------------------------------------------------
@@ -63,21 +65,26 b' class InlineBackend(InlineBackendConfig):'
63 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'],
68 default_value='png', config=True,
69 help="""The image format for figures with the inline
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"}:
72 def _figure_formats_changed(self, name, old, new):
73 from IPython.core.pylabtools import select_figure_formats
74 if 'jpg' in new or 'jpeg' in new:
75 75 if not pil_available():
76 76 raise TraitError("Requires PIL/Pillow for JPG figures")
77 77 if self.shell is None:
78 78 return
79 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 89 quality = Int(default_value=90, config=True,
83 90 help="Quality of compression [10-100], currently for lossy JPEG only.")
@@ -119,6 +119,8 b" PNG64 = b'iVBORw0KG'"
119 119 JPEG = b'\xff\xd8'
120 120 # front of JPEG base64-encoded
121 121 JPEG64 = b'/9'
122 # front of PDF base64-encoded
123 PDF64 = b'JVBER'
122 124
123 125 def encode_images(format_dict):
124 126 """b64-encodes images in a displaypub format dict
@@ -136,7 +138,7 b' def encode_images(format_dict):'
136 138
137 139 format_dict : dict
138 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 142 is base64-encoded.
141 143
142 144 """
@@ -156,6 +158,13 b' def encode_images(format_dict):'
156 158 jpegdata = encodebytes(jpegdata)
157 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 168 return encoded
160 169
161 170
@@ -69,10 +69,12 b' def test_encode_images():'
69 69 # invalid data, but the header and footer are from real files
70 70 pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
71 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 74 fmt = {
74 75 'image/png' : pngdata,
75 76 'image/jpeg' : jpegdata,
77 'application/pdf' : pdfdata
76 78 }
77 79 encoded = encode_images(fmt)
78 80 for key, value in iteritems(fmt):
General Comments 0
You need to be logged in to leave comments. Login now