[Zope] Returning Image Data from a Product

Terry Hancock hancock@anansispaceworks.com
Tue, 26 Feb 2002 13:18:53 -0800


Hi all,

I'm getting very confused about a Zope product I'm working
on, and I'm hoping somebody can share a clue on this.

I've already figured out that I can load an image included
with my product into PIL, with a line like this (which
took a long time to figure out, any better solutions to
this would be appreciated, too):

im = PIL.Image.open( Globals.ImageFile('www/myimg.png', globals()).path)

and then, I can load a PIL image into a contained Zope Image
object (I was actually figuring that Globals.ImageFile would
load a Zope image, but it seems not -- weird. Instead it's
some kind of file access object, hence the "path" attribute
used above).

                imf = StringIO()
                im.save(imf, "PNG")
                imf.seek(0)

                self.result_image.data    = imf.read()
                self.result_image.width   = 32
                self.result_image.height  = 32
                self.result_image.content_type='image/png'
                self.result_image.size    = len(self.result_image.data) 

("im" is the PIL image, "self.result_image" is the Zope image,
  and "imf" is a StringIO object containing the image).

Now the part that stumps me -- how do I return this image
back to the caller?  Specifically:

* I want to return the PNG Image (i.e. a block of data *
* with mime type 'image/png'), NOT an HTML image tag   *
* containing a reference to result_image .             *

I've tried:

   return self.result_image

which returns a serialized image tag (i.e. just like

   return self.result_image.tag()

which I've also tried).

   return self.result_image.data

returns the PNG image alright, but the mime type is
set to 'text/plain'!

The kicker is, I have a related module which does do
this successfully, by simply using:
 
   return self.result_image

But I can't figure out why this one doesn't do it. So
I need to have a better understanding of what makes
this happen in order to figure out what the deal is.
There are a lot of (apparently) trivial differences
between the two, but each time I come across something
that looks like the critical difference, it turns out
not to change anything, so it's pretty darned tedious
to do the experimental approach. So I figure I could
use some theoretical predictions to guide me. :)

Some of the differences I've already tried:

   Changing the base class from SimpleItem to Image
   (this is the class of the containing object, not
   the zope image object which is initialized within
   it -- i.e.:

class myspecial_image_maker(Image):  <-- this is the superclass that
                                         I changed from SimpleItem
                                         this class is not viewable
                                         as an image, so has no
                                         particular reason to be one.

	def __init__(self, id, title):
		self.id = id
		self.title = title
		self.result_image =  OFS.Image.Image(
                                        'result_image',
                                        'Result Image', 
                                        '', 
                                        content_type='image/png')


Also, on my earlier class, I want to use a URL interface for
caching reasons, so I overloaded __getattr__, like this:

def __getattr__(self, keystr):
	     try:
		# omitted lengthy code to catch my coded URLs

		# code like that quoted above to load/make a PIL
		# image and pack into the Zope result_image image.

		return self.result_image

	     except:
		Image.__getattr__(self, keystr)	

(So if anything goes wrong, or my code doesn't catch the 
attribute, it falls through to the superclass's __getattr__ --
this is pretty nice, but also tedious to debug, I have to
warn you -- if you snare up standard attributes with your
__getattr__, Zope gets very confused!).

I remind you that this code does what I wanted -- i.e when
you point your browser at the desired "coded" URL, you
get the PNG image itself, with correct 'image/png' mime
type. So you can do something like this:

<img src="<dtml-var expr="Avatars.anonymous_png">/s32" width="32"
     height="32" border="0"/>

where "anonymous_png" is an object of my earlier class. The coded
URL "s32" asks for a version of the image resized to 32x32, which
PIL takes care of.  This code *works*.

Now I'm trying to do it again, calling another function to generate
an image from scratch (i.e. not contained in the class), based on
some rules and PIL code, generated from the coded URL.  But for
some reason this *doesn't* work, and I'm trying to figure out what
determines this, so I can see if I can find the difference (not
to mention that this sounds like I will learn something significant
about Zope).
		
As you can see, I also changed the __getattr__ to use Image
and not SimpleItem.

Some of my test code, in order to avoid the complexity of
interacting with the __getattr__ code, returns the image
from a normal python method, like this:

class ...
    def testimg(self):
	"Need this docstring or zope complains ;)"
	
	# omitted code (as above) to load a PIL image and convert
	# to a zope image

	return self.result_image

This returns the image *tag* every time I've tried it.

Is it possible that it matters whether I'm using __getattr__ or
a python method to return this?  Is there some hidden difference
that I just haven't found yet (perhaps completely unrelated
to what I've shown).  The whole module is a bit long to post
on the list, and it includes a lot of trivial details that
would probably hinder more than help.  I'm hoping I've already
included the relevant parts above.

I think I've tried returning the image from the __getattr__
method (to no avail), but I've been through so many
variations, I really can't remember now!

BTW, the actual module size is:
% wc NaryaImage.py
    385    1284   11050 NaryaImage.py

Thanks for any ideas!

Terry

-- 
------------------------------------------------------
Terry Hancock
hancock@anansispaceworks.com       
Anansi Spaceworks                 
http://www.anansispaceworks.com 
P.O. Box 60583                     
Pasadena, CA 91116-6583
------------------------------------------------------