diff --git a/IPython/core/display.py b/IPython/core/display.py index b871906..f853f73 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -271,6 +271,24 @@ def display_javascript(*objs, **kwargs): """ _display_mimetype('application/javascript', objs, **kwargs) + +def display_pdf(*objs, **kwargs): + """Display the PDF representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw javascript data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('application/pdf', objs, **kwargs) + + #----------------------------------------------------------------------------- # Smart classes #----------------------------------------------------------------------------- diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index 9a2644c..a54fa42 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -93,6 +93,7 @@ class DisplayFormatter(Configurable): HTMLFormatter, SVGFormatter, PNGFormatter, + PDFFormatter, JPEGFormatter, LatexFormatter, JSONFormatter, @@ -116,6 +117,7 @@ class DisplayFormatter(Configurable): * text/latex * application/json * application/javascript + * application/pdf * image/png * image/jpeg * image/svg+xml @@ -766,11 +768,29 @@ class JavascriptFormatter(BaseFormatter): print_method = ObjectName('_repr_javascript_') + +class PDFFormatter(BaseFormatter): + """A PDF formatter. + + To defined the callables that compute to PDF representation of your + objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type` + or :meth:`for_type_by_name` methods to register functions that handle + this. + + The return value of this formatter should be raw PDF data, *not* + base64 encoded. + """ + format_type = Unicode('application/pdf') + + print_method = ObjectName('_repr_pdf_') + + FormatterABC.register(BaseFormatter) FormatterABC.register(PlainTextFormatter) FormatterABC.register(HTMLFormatter) FormatterABC.register(SVGFormatter) FormatterABC.register(PNGFormatter) +FormatterABC.register(PDFFormatter) FormatterABC.register(JPEGFormatter) FormatterABC.register(LatexFormatter) FormatterABC.register(JSONFormatter) @@ -789,6 +809,7 @@ def format_display_data(obj, include=None, exclude=None): * text/latex * application/json * application/javascript + * application/pdf * image/png * image/jpeg * image/svg+xml diff --git a/IPython/core/tests/test_formatters.py b/IPython/core/tests/test_formatters.py index 184d5d5..710ce1b 100644 --- a/IPython/core/tests/test_formatters.py +++ b/IPython/core/tests/test_formatters.py @@ -8,7 +8,9 @@ except: numpy = None import nose.tools as nt -from IPython.core.formatters import PlainTextFormatter, HTMLFormatter, _mod_name_key +from IPython.core.formatters import ( + PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key +) from IPython.utils.io import capture_output class A(object): @@ -279,4 +281,11 @@ def test_warn_error_pretty_method(): nt.assert_in("text/plain", captured.stderr) nt.assert_in("argument", captured.stderr) +class MakePDF(object): + def _repr_pdf_(self): + return 'PDF' +def test_pdf_formatter(): + pdf = MakePDF() + f = PDFFormatter() + nt.assert_equal(f(pdf), 'PDF') diff --git a/IPython/utils/jsonutil.py b/IPython/utils/jsonutil.py index 6de6709..9fed8d2 100644 --- a/IPython/utils/jsonutil.py +++ b/IPython/utils/jsonutil.py @@ -119,6 +119,8 @@ PNG64 = b'iVBORw0KG' JPEG = b'\xff\xd8' # front of JPEG base64-encoded JPEG64 = b'/9' +# front of PDF base64-encoded +PDF64 = b'JVBER' def encode_images(format_dict): """b64-encodes images in a displaypub format dict @@ -136,7 +138,7 @@ def encode_images(format_dict): format_dict : dict A copy of the same dictionary, - but binary image data ('image/png' or 'image/jpeg') + but binary image data ('image/png', 'image/jpeg' or 'application/pdf') is base64-encoded. """ @@ -156,6 +158,13 @@ def encode_images(format_dict): jpegdata = encodebytes(jpegdata) encoded['image/jpeg'] = jpegdata.decode('ascii') + pdfdata = format_dict.get('application/pdf') + if isinstance(pdfdata, bytes): + # make sure we don't double-encode + if not pdfdata.startswith(PDF64): + pdfdata = encodebytes(pdfdata) + encoded['application/pdf'] = pdfdata.decode('ascii') + return encoded diff --git a/IPython/utils/tests/test_jsonutil.py b/IPython/utils/tests/test_jsonutil.py index cdb5a7c..012b168 100644 --- a/IPython/utils/tests/test_jsonutil.py +++ b/IPython/utils/tests/test_jsonutil.py @@ -69,10 +69,12 @@ def test_encode_images(): # invalid data, but the header and footer are from real files pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82' jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9' + pdfdata = b'%PDF-1.\ntrailer<>]>>>>>>' fmt = { 'image/png' : pngdata, 'image/jpeg' : jpegdata, + 'application/pdf' : pdfdata } encoded = encode_images(fmt) for key, value in iteritems(fmt):