Repository: ipython/ipython

Issue:
IPython qtconsole (0.12) can't export to html with external png
This error looks a bit like that in issue #1087 but the details are different. I create a qtconsole session with some embedded figures. Export to HTML with inline PNG works fine. Export to XHTML/SVG 'works' (with missing images). Export to HTML with external PNG creates a folder with the PNG files correctly but fails to write the HTML (leaving a zero size .html file) with the following error:

```
Error exporting HTML to /Users/claumann/Desktop/test/test.html
'ascii' codec can't decode byte 0xc2 in position 2287: ordinal not in range(128)
```

I have an html (with embedded PNG) that constitutes a minimal session reproducing this but I don't know how to attach it to this issue. The (non-graphical) content:

```
In [1]: figure()
scatter(r_[0,1,2], r_[0,1,2], marker='x')
display(gcf())

<embedded image>

In [2]: !uname -a
Darwin blackbear.local 11.2.0 Darwin Kernel Version 11.2.0: Tue Aug  9 20:54:00 PDT 2011; root:xnu-1699.24.8~1/RELEASE_X86_64 x86_64

In [3]: np.__version__
Out[3]: '2.0.0.dev-26aa3cf'

In [4]: pylab.__version__
Out[4]: '2.0.0.dev-26aa3cf'

In [5]: matplotlib.__version__
Out[5]: '1.2.x'
```



Discussion:
I can confirm the problem, and it's truly weird: if I type this:

``` python
scatter(r_[0,1,2], r_[0,1,2], marker='x')
```

everything seems to work fine.  But if instead I do this:

``` python
figure()
scatter(r_[0,1,2], r_[0,1,2], marker='x')
```

then saving with external PNG produces the error you indicate above.  Now, those two calls should be the same from our perspective, so I really don't get what's going on.  We'll have to dig in to figure this out, but obviously we can't release 0.12 with broken qt console export.

Thanks for the timely report!

simple reproduce:

```
In [1]: u'é'
In [2]: plot(rand(100))
<save>
```

I've narrowed it down to image_tag returning unicode, but re.sub not handling that properly.  Ensuring that image_tag returns utf8-encoded str solves the issue, and I'm right now just trying to figure out the best way to do it.

Actually, I just confirmed that this bug exists since 0.11, so I'm bringing the priority down to high.  If the bug has been in the wild for 4 months, I don't want to hold the 0.12 release just for it.

But still, it is real and serious, so we should definitely try to address it ASAP.  With a bit of luck we'll have it fixed before the release, but since that's coming this weekend, it may not quite happen right away.

Ah, yes: the re library is known to have unicode issues, there's even a proposed new regexp library for python itself partly due to these problems.  Thanks for getting onto this one!


Resolving patch:
diff --git a/IPython/frontend/qt/rich_text.py b/IPython/frontend/qt/rich_text.py
--- a/IPython/frontend/qt/rich_text.py
+++ b/IPython/frontend/qt/rich_text.py
@@ -11,6 +11,9 @@
 # System library imports.
 from IPython.external.qt import QtGui
 
+# IPython imports
+from IPython.utils import py3compat
+
 #-----------------------------------------------------------------------------
 # Constants
 #-----------------------------------------------------------------------------
@@ -92,7 +95,6 @@ def export(self):
                     box.setInformativeText(info)
                     box.addButton(ib, QtGui.QMessageBox.NoRole)
                     box.addButton(eb, QtGui.QMessageBox.YesRole)
-                    box.setDefaultButton(ib)
                     layout.setSpacing(0)
                     layout.addWidget(box)
                     layout.addWidget(checkbox)
@@ -141,6 +143,8 @@ def export_html(html, filename, image_tag = None, inline = True):
     """
     if image_tag is None:
         image_tag = default_image_tag
+    else:
+        image_tag = ensure_utf8(image_tag)
 
     if inline:
         path = None
@@ -172,6 +176,8 @@ def export_xhtml(html, filename, image_tag=None):
     """
     if image_tag is None:
         image_tag = default_image_tag
+    else:
+        image_tag = ensure_utf8(image_tag)
 
     with open(filename, 'w') as f:
         # Hack to make xhtml header -- note that we are not doing any check for
@@ -210,6 +216,20 @@ def default_image_tag(match, path = None, format = "png"):
     return ''
 
 
+def ensure_utf8(image_tag):
+    """wrapper for ensuring image_tag returns utf8-encoded str on Python 2"""
+    if py3compat.PY3:
+        # nothing to do on Python 3
+        return image_tag
+    
+    def utf8_image_tag(*args, **kwargs):
+        s = image_tag(*args, **kwargs)
+        if isinstance(s, unicode):
+            s = s.encode('utf8')
+        return s
+    return utf8_image_tag
+
+
 def fix_html(html):
     """ Transforms a Qt-generated HTML string into a standards-compliant one.
 


Test patch: