Repository: python-pillow/Pillow

Issue:
PSDraw.PSDraw().image() error on Python 3.4
I was trying to run [this simple postscript example](http://pillow.readthedocs.org/en/latest/handbook/tutorial.html#drawing-postscript) in the overview on Python 3.4, but after replacing `lena.ppm` with a valid image name I immediately encountered the following error: 

```
Traceback (most recent call last):
  File "test.py", line 15, in <module>
    ps.image(box, im, 75)
  File "[path-to-virtualenv]/lib/python3.4/site-packages/PIL/PSDraw.py", line 144, in image
    EpsImagePlugin._save(im, self.fp, None, 0)
  File "[path-to-virtualenv]/lib/python3.4/site-packages/PIL/EpsImagePlugin.py", line 406, in _save
    fp.flush()
  TypeError: must be str, not bytes
```

Reporting this as a bug because its a common python3 error for ported code, but let me know if you have any trouble reproducing or if I'm doing something silly.

Cheers,
-Mike

Setup: python3.4 (virtualenv), Mac OSX 10.1, Pillow 2.6.1



Discussion:

Resolving patch:
diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py
--- a/PIL/PSDraw.py
+++ b/PIL/PSDraw.py
@@ -35,25 +35,31 @@ def __init__(self, fp=None):
             fp = sys.stdout
         self.fp = fp
 
+    def _fp_write(self, to_write):
+        if bytes is str:
+            self.fp.write(to_write)
+        else:
+            self.fp.write(bytes(to_write, 'UTF-8'))
+
     def begin_document(self, id=None):
         """Set up printing of a document. (Write Postscript DSC header.)"""
         # FIXME: incomplete
-        self.fp.write("%!PS-Adobe-3.0\n"
-                      "save\n"
-                      "/showpage { } def\n"
-                      "%%EndComments\n"
-                      "%%BeginDocument\n")
-        # self.fp.write(ERROR_PS)  # debugging!
-        self.fp.write(EDROFF_PS)
-        self.fp.write(VDI_PS)
-        self.fp.write("%%EndProlog\n")
+        self._fp_write("%!PS-Adobe-3.0\n"
+                       "save\n"
+                       "/showpage { } def\n"
+                       "%%EndComments\n"
+                       "%%BeginDocument\n")
+        # self.fp_write(ERROR_PS)  # debugging!
+        self._fp_write(EDROFF_PS)
+        self._fp_write(VDI_PS)
+        self._fp_write("%%EndProlog\n")
         self.isofont = {}
 
     def end_document(self):
         """Ends printing. (Write Postscript DSC footer.)"""
-        self.fp.write("%%EndDocument\n"
-                      "restore showpage\n"
-                      "%%End\n")
+        self._fp_write("%%EndDocument\n"
+                       "restore showpage\n"
+                       "%%End\n")
         if hasattr(self.fp, "flush"):
             self.fp.flush()
 
@@ -66,18 +72,11 @@ def setfont(self, font, size):
         """
         if font not in self.isofont:
             # reencode font
-            self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %
-                          (font, font))
+            self._fp_write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %
+                           (font, font))
             self.isofont[font] = 1
         # rough
-        self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font))
-
-    def setink(self, ink):
-        """
-        .. warning:: This has been in the PIL API for ages but was never implemented.
-
-        """
-        print("*** NOT YET IMPLEMENTED ***")
+        self._fp_write("/F0 %d /PSDraw-%s F\n" % (size, font))
 
     def line(self, xy0, xy1):
         """
@@ -86,7 +85,7 @@ def line(self, xy0, xy1):
         left corner of the page).
         """
         xy = xy0 + xy1
-        self.fp.write("%d %d %d %d Vl\n" % xy)
+        self._fp_write("%d %d %d %d Vl\n" % xy)
 
     def rectangle(self, box):
         """
@@ -101,7 +100,7 @@ def rectangle(self, box):
 
                         %d %d M %d %d 0 Vr\n
         """
-        self.fp.write("%d %d M %d %d 0 Vr\n" % box)
+        self._fp_write("%d %d M %d %d 0 Vr\n" % box)
 
     def text(self, xy, text):
         """
@@ -111,7 +110,7 @@ def text(self, xy, text):
         text = "\\(".join(text.split("("))
         text = "\\)".join(text.split(")"))
         xy = xy + (text,)
-        self.fp.write("%d %d M (%s) S\n" % xy)
+        self._fp_write("%d %d M (%s) S\n" % xy)
 
     def image(self, box, im, dpi=None):
         """Draw a PIL image, centered in the given box."""
@@ -135,14 +134,14 @@ def image(self, box, im, dpi=None):
             y = ymax
         dx = (xmax - x) / 2 + box[0]
         dy = (ymax - y) / 2 + box[1]
-        self.fp.write("gsave\n%f %f translate\n" % (dx, dy))
+        self._fp_write("gsave\n%f %f translate\n" % (dx, dy))
         if (x, y) != im.size:
             # EpsImagePlugin._save prints the image at (0,0,xsize,ysize)
             sx = x / im.size[0]
             sy = y / im.size[1]
-            self.fp.write("%f %f scale\n" % (sx, sy))
+            self._fp_write("%f %f scale\n" % (sx, sy))
         EpsImagePlugin._save(im, self.fp, None, 0)
-        self.fp.write("\ngrestore\n")
+        self._fp_write("\ngrestore\n")
 
 # --------------------------------------------------------------------
 # Postscript driver


Test patch:
diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py
new file mode 100644
--- /dev/null
+++ b/Tests/test_psdraw.py
@@ -0,0 +1,50 @@
+from helper import unittest, PillowTestCase
+
+
+class TestPsDraw(PillowTestCase):
+
+    def test_draw_postscript(self):
+
+        # Based on Pillow tutorial, but there is no textsize:
+        # http://pillow.readthedocs.org/en/latest/handbook/tutorial.html
+
+        # Arrange
+        from PIL import Image
+        from PIL import PSDraw
+        tempfile = self.tempfile('temp.ps')
+        fp = open(tempfile, "wb")
+
+        im = Image.open("Tests/images/hopper.ppm")
+        title = "hopper"
+        box = (1*72, 2*72, 7*72, 10*72)  # in points
+
+        # Act
+        ps = PSDraw.PSDraw(fp)
+        ps.begin_document(title)
+
+        # draw diagonal lines in a cross
+        ps.line((1*72, 2*72), (7*72, 10*72))
+        ps.line((7*72, 2*72), (1*72, 10*72))
+
+        # draw the image (75 dpi)
+        ps.image(box, im, 75)
+        ps.rectangle(box)
+
+        # draw title
+        ps.setfont("Courier", 36)
+        ps.text((3*72, 4*72), title)
+
+        ps.end_document()
+        fp.close()
+
+        # Assert
+        # Check non-zero file was created
+        import os
+        self.assertTrue(os.path.isfile(tempfile))
+        self.assertGreater(os.path.getsize(tempfile), 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
+
+# End of file