[Zope-Annce] VarImage 2 Released

Terry Hancock hancock@anansispaceworks.com
Sun, 13 Apr 2003 23:36:19 -0700


The much-improved version 2 of VarImage ("Variable Image")
is now available, including generative image support, chainable
image operations, color map and transposition effects, and more!

It also includes an extremely simple plugin mechanism, so it is
now easy to add your own operators to extend VarImage.

Download from:
  http://www.anansispaceworks.com/papers_html
or:
  http://www.zope.org/Members/terry/VarImage

More information about VarImage ...

VarImage 2.0-beta

  VarImage means "variable image".  It provides a way to make
  on-the-fly modifications to an image in Zope simply by referring
  to the correct URL.

  Starting in this version (version 2), VarImage also allows "component"
  images to be loaded into a sub-folder called "components". These
  may be composited onto the main image to provide generative image
  effects.

  Version 2 also includes experimental code for referrer-blocking
  (this means that you can keep remote sites from directly linking
  to your images).

Syntax

  The URL-command is parsed as a chain of "operations" to be applied
  to the original image, separated by "." (period).  Each operation
  is specified as an "operator", followed by zero or more "arguments",
  separated by "_".

  Arguments and operators must be composed of only letters and numbers
  ([A-Za-z0-9], note that "_" and "." are not legal because they are
  used as separators). Other than the fact that floating point values
  have to use "o" (the letter "o") instead of "." for the decimal
  point, this works well.

  Colors are represented by strings, usually as hexadecimal codes, with
  a prefix of "x" and 6 hexadecimal characters.

  Component images may be referred to by their unadorned Zope ID, so
  "myimage/components/mycomponent" is simply "mycomponent".  Note
  that, to be useful, you have to use only letters and numbers in
  the name of components.
  
  Each operation is applied in sequence, to the result of the previous
  operation, and then the final result is returned.

Examples

  If you have a VarImage "shinobu_jpg", you can get it
  in an exact size of 100x100 (regardless of the original aspect)::

    shinobu_jpg/s_100
 
  or 100x50::

    shinobu_jpg/s_100_50

  or you can thumbnail it into a 70x50 space (the longer dimension
  will be determined by the specified limit, while the smaller is
  determine by the image aspect ratio -- i.e. the image will "fit"
  into the available 70x50 space)::

    shinobu_jpg/f_70_50

  or you can set the horizontal or vertical dimensions::

    shinobu_jpg/h_85
    shinobu_jpg/v_100

  (The above examples are converted from the Version 1 docs).

  You can also convert the image to PNG or GIF format::

    shinobu_jpg/tn_100.png
    shinobu_jpg/tn_100_200.gif

  And do color effects, such as black-and-white::

    shinobu_jpg/bw

  or color-multiplication::

    shinobu_jpg/mult_x0e5f00.png
    shinobu_jpg/bw.R.gif

Special Commands

  For a help page with a complete list of all currently understood
  operators, use::

    shinobu_jpg/help

  If you need to clear the cache and start over for some reason (this
  is necessary a lot when you're writing a new plugin)::

    shinobu_jpg/clear_cache

  And you may set the "fovial" region of the image (used by the "tn"
  thumbnail operator to show the center-of-interest/fovial-extent)::

    shinobu_jpg/set_fovia?x=100&y=150&dx=30&dy=30

  Or you can call this from Zope::
  
    <dtml-call expr="shinobu_jpg.set_fovia( (100,150,30,30) )">

Calling VarImages

  After writing the above, I have discovered that what does *not*
  work is calling the image as::

    <dtml-var expr="shinobu_jpg.f_100">

  (Actually it will work *if* you had previously generated the image,
  but it will not regenerate after the cache expires, which can bite
  you!).

  However, you can do this instead::

    <dtml-var expr="shinobu_jpg.restrictedTraverse('f_100')">

  Not as pretty, I know, but it will correctly regenerate, and
  you still get the benefit of the Zope image tag generation (the
  image tag will have correct width and height specified, which
  is handy, since with the f ("fit") command, you don't know
  them in advance (you know only the maximum possible values).

  I do consider this a bug, but it's low priority because the
  workaround exists.

How It Works

  VarImage inherits from Zope's Image and ObjectManager types, so
  it is an image that also acts as a folder. The folder contains
  a Temporary Folder named "cache". When you query for a modified
  image, it first checks to see if the cache already contains the
  image. If so, it returns that. Otherwise, it uses Python Imaging
  Library to generate the image (storing it in the cache of course).
  
  A couple of other cases are caught, too: most plugins will try
  to catch "do-nothing" cases and pass through without operating.
  
  If you request an image *while* it is being generated, the
  requesting thread will sleep until the generation is complete
  and then return the result.

  Temporary Folder uses a RAM storage method which will lose objects
  when they are not in use (I don't know what algorithm it actually
  uses, though I'm hoping it's some variant of "least recently
  used"), or whenever Zope is restarted (this is good).  Thus the
  cached objects are never permanently added to Zope.

  Note that because each resized image gets a unique URL, front
  end caches (proxy or browser) can efficiently cache these images,
  thus further reducing regeneration to the minimum needed.

Writing Plugins

  It is now trivial to add a plugin operator to VarImage, and you
  do not need any understanding of Zope to do this. You will need
  to understand how to manipulate and process images in Python.

  It is not strictly necessary to use Python Imaging Library, but
  that is what I have used for all the example plugins, as it is
  the best Python library available as of this writing. The next
  best option is probably libmagick for C, which has some Python
  wrappers defined for it --- however, the Python wrappers were
  very immature or in poor repair when I wrote this.
  
  I would certainly welcome contributed libmagick plugins, though!

  To create a new operator, you will create an image processing
  function with the following profile::

    imio, imtype, xsz, ysz = your_function(imio, imtype, xsz, ysz, args)

  Where:

    imio -- is a StringIO (file-like) object containing the image data.
    
    imtype -- is the image MIME type (e.g. "image/jpeg").
    
    xsz  -- is the width of the image.
    
    ysz  -- is the height of the image.

    args -- is a tuple of converted arguments from the URL.

  Then you will register it with the 'Operator' class constructor::

    Operator('foo', your_function, 0, 4, (int, int, float, str))

  This example will register your operator under the name 'foo', 
  accepting 0 to 4 arguments, with the specified types.  The arguments
  will be converted from strings to these types before being passed
  to your function, so you won't normally have to parse them unless
  you need something special (in which case, you use "str").

  The plugin "pil_resize.py" was the first, and best documented, so
  I recommend you look at that one for comments and examples.

  Operators that use components, or need to access the image fovia
  information have some special requirements --- see "Operators/pil_composite.py"
  and "Operators/pil_fovia.py" for examples.

  The function and the registration should be in a single Python module,
  which you will put into the 'Operators' package within VarImage.

  That's it --- if your code works, you'll have a new VarImage
  operator defined.  Be sure to provide a (structured-text) docstring
  in your function, as this will be included in the automatic
  documentation produced by the "help" method.  

Requirements

  Currently, you need Zope 2.5.1 or higher and Python 2.1.3 is
  recommended.  You'll need Python Imaging Library 1.1.3 or
  higher for all the provided plugins to work.

  GIF support is spotty --- especially for transparency --- in
  P.I.L. 1.1.3 (which is what I developed against), though I
  have read that 1.1.4 may address this, and is due to come
  out soon as of this writing.  You might want to try it.

  This in turn requires the zlib, PNG, and JPEG libraries. I had
  real problems getting P.I.L. to find the libraries if they are
  not installed in one of the canonical locations such as
  /usr/local, so here's a heads-up -- you'll have to edit the
  Makefile and Setup.in manually for it to work right.  Hopefully
  that'll get fixed in a later version of P.I.L.

Author and Licensing

  Terry Hancock wrote VarImage for Anansi Spaceworks. It may be
  redistributed under the terms of the Gnu Public License. The
  full text of the GPL is in the file LICENSE.txt in this
  distribution.



--
Terry Hancock ( hancock at anansispaceworks.com )
Anansi Spaceworks  http://www.anansispaceworks.com