[Zope] - Image object enhancements (patch included!)

Ty Sarna tsarna@endicor.com
Mon, 7 Dec 1998 16:02:39 -0600 (CST)


Christopher G. Petrilli wrote:
> What I want to be able to do is have a folder that contains images as a
> subfolder to my current folder.  Now I know I can reference them, no
> problem: <!--#var "images.logo"-->  but that doesn't give the right SRC=
> line in the IMG directive.  Now I sat and thought about this, but I'm
> just not sure how an image is supposed to find itself in the hierarchy
> so it can publish the right URL to find itself.  

This is related to another problem, which is that if you have:

Folder: /site
	Doc: standard_html_header  (contains "<!--#var logo-->")
	Image: logo
	Doc: index_html ("<!--#var standard_html_header-->")
	Folder: foo
		Doc: index_html ("<!--#var standard_html_header-->")
		Folder: bar
			Doc: index_html ("<!--#var standard_html_header-->")

If you go look at http://localhost/site, http://localhost/site/foo/, and
http://localhost/site/foo/bar/, your browser will need to download
http://localhost/site/logo, http://localhost/site/foo/logo, and
http://localhost/site/foo/bar/logo. It doesn't know that they're the same
object, since they're differently-named.

The __str__ method on images really needs to figure out the true
absolute path of the object and use that. (like <IMG SRC="/site/logo">)

...

Speaking of things that need to be done about Image objects, the fact that
they don't send a size can be annoying to users of a site.  When the
image tag includes a width and height, the browser can lay out the page
without waiting to see how large the image is.  I had trouble falling
asleep last night, so I got up and thought I'd take a look at how hard
it'd be to implement.  Shortly thereafter, I had the patches below.  :^)

Image now autodetects the dimensions of uploaded GIF or PNG images (JPEG
is harder and I use many more GIFs than JPEGs, so I punted on that.
Someone else can add it if they feel ambitious). For pre-existing
objects, you can make the object compute (or recompute) the dimensions
by going into the Edit tab, clearing the dimension inputs, and
submitting. You can also manually set the dimensions to something else
if you like, though this is not generally recommended as a way to scale
images.

If Image is unable to figure out the dimensions (JPEG, or some other
unknown format), the width and heigh remain unset and the IMG tag is
generated without them.

Maybe next time I'm insomniac I'll look at the absolute oath issue :^)


--- Image.py.orig	Sun Dec  6 23:16:46 1998
+++ Image.py	Mon Dec  7 08:20:04 1998
@@ -93,7 +93,7 @@
 from Globals import Persistent
 from Acquisition import Implicit
 from DateTime import DateTime
-import string
+import string, struct
 
 manage_addFileForm=HTMLFile('imageAdd', globals(),Kind='File',kind='file')
 def manage_addFile(self,id,file,title='',precondition='',REQUEST=None):
@@ -157,6 +157,7 @@
         self.title=title
         if precondition: self.precondition=precondition
         self.size=len(self.data)
+        self._post_upload()
 
     def id(self): return self.__name__
 
@@ -187,12 +188,15 @@
         """
         raise 'Redirect', URL1
 
-    def manage_edit(self,title,content_type,precondition='',REQUEST=None):
+    def manage_edit(self,title,content_type,precondition='',width='',height='',REQUEST=None):
         """
         Changes the title and content type attributes of the File or Image.
         """
         self.title=title
         self.content_type=content_type
+        self.width = width
+        self.height = height
+        self._intuit_image_size()
         if precondition: self.precondition=precondition
         elif self.precondition: del self.precondition
         if REQUEST: return MessageDialog(
@@ -211,6 +215,7 @@
         data=file.read()
         self.data=Pdata(data)
         self.size=len(data)
+        self._post_upload()
         if REQUEST: return MessageDialog(
                     title  ='Success!',
                     message='Your changes have been saved',
@@ -226,6 +231,7 @@
         'handle PUT requests'
         self.data=Pdata(BODY)
         self.size=len(BODY)
+        self._post_upload()
         try:
             type=REQUEST['CONTENT_TYPE']
             if type: self.content_type=type
@@ -246,6 +252,10 @@
         """
         return self.content_type
 
+    def _post_upload(self):
+        # hook can be overridden in subclasses
+        pass
+
     def size(self):    return len(self.data)
     def __str__(self): return str(self.data)
     def __len__(self): return 1
@@ -263,7 +273,7 @@
     if REQUEST is not None: return self.manage_main(self,REQUEST)
 
 class Image(File):
-    """Principia object for *Images*, can be GIF or JPEG.  Has the same
+    """Principia object for *Images*, can be GIF, PNG, or JPEG.  Has the same
     methods as File objects.  Images also have a string representation
     that renders an HTML 'IMG' tag.
     """
@@ -282,8 +292,36 @@
                                kind='image')
     manage=manage_main=manage_editForm
 
+    def _post_upload(self):
+        self.width = self.height = ''
+        self._intuit_image_size()
+
     def __str__(self):
-        return '<IMG SRC="%s" ALT="%s">' % (self.__name__, self.title_or_id()) 
+        w = h = ''
+        if hasattr(self, 'width') and len(self.width):
+            w = 'WIDTH="%s" ' % self.width
+        if hasattr(self, 'height') and len(self.height):
+            h = 'HEIGHT="%s" ' % self.height
+
+        return '<IMG SRC="%s" %s%sALT="%s">' % (self.__name__, w, h, self.title_or_id())
+
+    def _intuit_image_size(self):
+        w = h = ''
+        
+        # handle GIFs   
+        if (self.size >= 10) and self.data[:6] in ('GIF87a', 'GIF89a'):
+            w, h = struct.unpack("<HH", self.data[6:10])
+            w = str(int(w)); h = str(int(h))
+
+        # handle PNGs
+        if (self.size >= 16) and (self.data[:8] == '\x89PNG\r\n\x1a\n'):
+            w, h = struct.unpack(">LL", self.data[8:16])
+            w = str(int(w)); h = str(int(h))
+
+        if not hasattr(self, 'width') or not len(self.width):
+            self.width = w
+        if not hasattr(self, 'height') or not len(self.height):
+            self.height = h
 
 def cookId(id, title, file):
     if not id and hasattr(file,'filename'):
@@ -306,3 +344,5 @@
     def __len__(self):
         return len(self.data)
 
+    def __getslice__(self, i, j):
+        return self.data[i:j]
--- imageEdit.dtml.orig	Sun Dec  6 23:08:03 1998
+++ imageEdit.dtml	Sun Dec  6 23:17:22 1998
@@ -18,6 +18,20 @@
       </TD>
     </TR>
     <TR>
+      <TH ALIGN="LEFT" VALIGN="TOP"><EM>Width</EM></TH>
+      <TD ALIGN="LEFT" VALIGN="TOP">
+        <INPUT TYPE="STRING" NAME="width" SIZE="10"
+          VALUE="<!--#if width--><!--#var width--><!--#/if-->">
+      </TD>
+    </TR>
+    <TR>
+      <TH ALIGN="LEFT" VALIGN="TOP"><EM>Height</EM></TH>
+      <TD ALIGN="LEFT" VALIGN="TOP">
+        <INPUT TYPE="STRING" NAME="height" SIZE="10"
+          VALUE="<!--#if height--><!--#var height--><!--#/if-->">
+      </TD>
+    </TR>
+    <TR>
       <TH ALIGN="LEFT" VALIGN="TOP">Content Type</TH>
       <TD ALIGN="LEFT" VALIGN="TOP">
         <INPUT TYPE="TEXT" NAME="content_type:required" SIZE="40"
--- imageView.dtml.orig	Sun Dec  6 23:24:31 1998
+++ imageView.dtml	Sun Dec  6 23:24:37 1998
@@ -5,7 +5,10 @@
 <BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
 <!--#var manage_tabs-->
 
-<IMG src="<!--#var id-->" ALT="<!--#var title_or_id-->">
+<IMG src="<!--#var id-->" 
+<!--#if width-->WIDTH="<!--#var width-->"<!--#/if-->
+<!--#if height-->HEIGHT="<!--#var height-->"<!--#/if-->
+ALT="<!--#var title_or_id-->">
 
 </BODY>
 </HTML>