The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Image::Shoehorn::Gallery - generate "smart" HTML slideshows from a directory of image files.

SYNOPSIS

 use Image::Shoehorn::Gallery;

 Image::Shoehorn::Gallery->create({
                                   source      => "~/my-images",
                                   directory   => "/htdocs/images",
                                   url         => "http://mysite.com/images",
                                   static      => 1,
                                   scales      => [
                                                   [ "thumb","75x50"  ],
                                                   [ "default", "50%" ],
                                                   [ "small","25%"    ],
                                                   [ "medium","50%"   ],
                                                ],
                                   scale_if    => { x => 400 , y => 300 },
                                   iptc        => ["headline","caption/abstract"],
                                   set_lang    => "en-ca",
                                   set_styles  => {
                                                  image => [
                                                            {title=>"my css",href=>"/styles.css"},
                                                           ],
                                                 },
                                   set_index_images => { default => 1 },
                                  });

DESCRIPTION

Image::Shoehorn::Gallery generates HTML slideshows from a directory of image files. But wait, there's more!

Image::Shoehorn uses XML::Filter::XML_Directory_2XHTML, XML::SAX::Machines and a small army of Image::* packages allowing you to :

  • Create one, or more, scaled versions of an image, and their associate HTML pages. Scaled version may also be defined but left to be created at a later date by Apache::Image::Shoehorn.

    Associate HTML are always "baked", rather than "fried" (see also : http://www.aaronsw.com/weblog/000404 )

  • Read a user-defined list of IPTC and EXIF metadata fields from each image and include the data in the HTML pages.

  • Generate named indices and next/previous links by reading IPTC "headline" data.

  • Define one, or more, SAX filters to be applied to "index" and individual "image" documents before they are passed the final XML::SAX::Writer filter for output.

    The default look and feel of the gallery pages is pretty plain, but you could easily define a "foofy design" XSL stylesheet to be applied with the XML::Filter::XSLT SAX filter:

     <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                     version = "1.0" >
    
      <xsl:template match = "/">
      <html>
       <xsl:copy-of select = "html/head" />
       <body>
    
        <!-- lots of foofy design stuff -->
        <xsl:copy-of select = "/html/body/*" />
        <!-- lots of foofy design stuff -->
    
       </body>
      </html>
      </xsl:template>
    
     </xsl:stylesheet>
  • Generates valid XHTML (strict) and CSS!

PACKAGE METHODS

__PACKAGE__->create(\%args)

This is the magic spell that will create your galleries.

Valid arguments are :

  • source

    String.

    This is the path to the directory that you want to read images from.

  • destination

    String.

    This is the path to directory that you want to write images, and HTML files, to. If undefined, then the value of source will be used.

  • directory

    String.

    Deprecated in favour of source and destination. If present, it will be used as *both* the source and destination directories.

  • url

    String.

    The URL that maps to directory on your webserver.

  • maxdepth

    Int.

    The maximum number of sub directories to munge and render.

  • static

    Boolean.

    Used in conjunction with the scales option for generating scaled versions of an image and their URLs.

    If false, or not defined, the package will assume that you have configured Apache::Image::Shoehorn to generate scaled versions of an image.

    If true, then the package will output image URLs that map to static images on the filesystem and ask Image::Shoehorn to create the new files, or update existing ones.

    Note, however, that the "thumb" (thumbnail) image will be generated regardless of whether or not you are using Apache::Image::Shoehorn. This is actually a feature since you would peg your machine having to create all those thumbnails the first time you loaded an especially large index page.

  • scales

    Array reference containing one, or more, array referece.

    Each of the child arrays should contain (2) elements :

    • name

      A name like "small" or "medium". This name is used as part of the naming scheme for images that have been scaled and their associate HTML pages.

      Names can be pretty much anything you'd like, with the exception of "thumb" and "default" which are discussed below.

    • scale

      These are required whether or not you are going to be generate static images. Even if you are going to render your images on the fly using Apache::Image::Shoehorn, the HTML spec (hi Karl) mandates that you provide height and widgth attributes for your img elements. So...

      Takes arguments in the same form as Image::Shoehorn which are, briefly :

      • n%

      • nxn

      • xn

      • nx

      There are two special scale names :

      • thumb

        You must define a thumb scale. It is used to generate thumbnails for the index page which are, in turn, used when generating the individual HTML pages for each image.

      • default

        This feature is only supported for images that are rendered statically :-(.

        Suppose your source images are very large and you would like to use a scaled version as the default image in your gallery. You may want to do this because you are concerned about people doing bad things with your high quality images or you don't want to pay the additional charges that your web-hosting service will charge you for all those 2-3 MB files. Or both.

        The default image is the default view and its dimensions are what all other scales are keyed off of.

        For example, your source image is 1200x840 and you define two scales (not including the 'thumb' scale.) The first is called 'small' and the second 'default'; both have a value of '50%'.

        Note: the hooks for creating default images are smart about paying attention to the scaleif options, discussed below.

        Since you have defined a default image, it will be created in your source directory with the same basename as the source image itself. It will be half the size of the original, 600x420. The 'small' version will be created and will be half the size of the 'default' image, rather than the source, or 300x210.

        Remember to use this feature carefully if your source and destination directories are the same. You could easily overwrite all your source images with newer default "sources".

  • scaleif

    Hash reference.

    Define height and width values that will be used to determine whether or not an image should actually be scaled.

    For example, it is unlikely that you will need to create a small version (say 25% the size of the original) if your source file is 100 by 150 pixels. You might - that's your business - but atleast this way you can opt out.

    Images will only be scaled if their height or width is greater than the height and/or width listed in this argument.

    You may define one or both of the following :

    • x

      Int.

      The minimum width that an image must have to be scaled.

    • y

      Int.

      The minimum height that an image must have to be scaled.

    Note that although multiple image files may not be created, if the source image is smaller than the dimensions passed in this argument, their associate HTML files will be generated. Don't worry, they'll point to the same unscaled image.

    Think of it as the glass being half full.

  • iptc

    Array reference.

    A list of IPTC fields to read from an image. Fields are presented in the order they are defined.

    For a complete list of IPTC fields, please consult the Image::IPTCInfo.

  • exif

    Array reference.

    A list of EXIF fields to read from an image. Fields are presented in the order they are defined.

    For a complete list of EXIF fields, please consult http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html

  • set_lang

    String.

    Set the language code for your HTML documents.

  • set_styles

    Hash reference.

    Used to override the default CSS for either and "index" page or an individual "image" page.

    Valid hash keys are :

    • index

    • image

    Where each key expects an array ref of hash refs whose keys are :

    • href

    • title

      Default is ""

    • rel

      Default is "stylesheet"

    • media

      Default is "all".

    Styles will be added in the order that they are defined in the array ref.

    The default CSS styles are outlined below.

  • set_filters

    Hash reference

    Define one or more additional filters to be applied to either an "index" or individual "image" page.

    Valid hash keys are :

    • index

    • image

    Filters are applied last, before events are finally handed off to XML::SAX::Writer and in the order that they are defined.

    Example:

     package MySAX;
     use base qw (XML::SAX::Base);
    
     sub start_element {
        my $self = shift;
        my $data = shift;
    
        $self->SUPER::start_element($data);
    
        if ($data->{Name} eq "body") {
           $self->SUPER::start_element({Name=>"h1"});
           $self->SUPER::characters({Data=>"hello world"});
           $self->SUPER::end_element({Name=>"h1"});
        }
     }
    
     package main;
    
     # The following will add <h1>hello world</h1>
     # at the top of all your 'image' pages. Woot!
    
     use Image::Shoehorn::Gallery;
     Image::Shoehorn::Gallery->create({
                                       # ...
    
                                       set_filters => { image => [ MySAX->new() ]},
                                      });
  • set_index_images

    Hash reference.

    Define images to associate with files in a directory listing. Valid keys are :

    • image

      Image to associate with a file whose media type is "image"

      Default is to generate and include a thumbnail, as defined by the "thumb" scale option (see above.)

    • directory

      Image to associate with a directory.

    • file

      Image to associate with a file whose media type is not "image"

      Example :

       # Use the default Apache icons
      
       my %images = (
                     directory => {
                                   src    => "/icons/dir.gif",
                                   height => "20",
                                   width  => "20",
                                   alt    => "ceci n'est pas un dossier",
                                  },
                     file => {
                              src    => "/icons/unknown.gif",
                              height => "20",
                              width  => "20",
                              alt    => "ceci n'est pas un fichier",
                             },
                     );
      
       Image::Shoehorn::Gallery->create({
                                         # ...
                                         set_index_images => \%images,
                                        });
    • default

      Boolean.

      This is just a shortcut to use the default image handler and the handlers for files and directories example described above.

      If you are not using Apache for your web server and/or have not aliased the Apache icons folder to /icons, it won't do you much good.

    Valid keys arguments are either :

    • hash reference

      Containing key/value pairs for the following image attributes :

      • src

      • height

      • width

      • alt

    • code reference

      The code reference will be passed the absolute path of the current image and is expected to return a hash reference similar to the one described above.

    This is an XML::Filter::XML_Directory_2XHTML-ism. Please consult docs for further details.

  • set_encoding

    String.

    Default is "UTF-8"

  • force

    Int.

    By default neither the scaled version of an image, nor the associate HTML files, will be created unless the source image has a more recent modification date.

    You can use this option to override this check.

    If the value is greater than zero, HTML files will be regenerated.

    If the value is greater than one, images and HTML files will be regenerated.

  • verbose

    Boolean.

NAMING CONVENTIONS

Let's say you've got an image named :

 20020603-my-new-toy.jpg

You've defined two "views" to be generated : small and medium. The following files will be created :

 20020603-my-new-toy.html
 20020603-my-new-toy-thumb.jpg **
 20020603-my-new-toy-small.jpg *
 20020603-my-new-toy-small.html
 20020603-my-new-toy-medium.jpg *
 20020603-my-new-toy-medium.html

 *  If you are rendering scaled images on the fly, with I<Apache::Image::Shoehorn>, 
    these files not be created until such a time as they are actually viewed

 ** Thumbnails are always generated, regardless of the I<static> flag. As mentioned 
    earlier, this is a feature. If you have a directory with many images, you will peg
    your web server the first time you have to render all those images for the index
    listing.

The package uses XML::Filter::XML_Directory_2XHTML which, a few steps up the inheritance tree, uses XML::Filter::XML_Directory_Pruner to exclude certain specific files from the directory (index) listing. The exact rule set currently used it :

  $xhtml->exclude(
                  starting => ["\\."],
                  ending   => ["html","tmp","~"],
                  # e.g. ending with "-thumb.jpg","-small.jpg" or "-medium.jpg"
                  matching => ["^(.*)-(".join("|","thumb",@{$views}).")\.([^\.]+)\$"],
                 );

The plan is to, eventually, teach XML::Filter::XML_Directory_Pruner to include and exclude widgets based on media type, at which point we could simply do :

 $xhtml->include( media => "image" );

But until then, it is recommended that you make sure your source images don't match the "matching" pattern describe above. Or if you just think I'm an idiot and have a better rule-set, send my a note and I'll probably include it.

CSS

The following CSS classes are defined for the HTML generated by the package.

They are provided as a reference in case you want to specify your own CSS stylesheet.

"index" page

 body {
      background-color: #ffffff;
      margin:0;
 }

 .breadcrumbs {
               display:block;
               background-color: #f5f5dc;
               padding:5px;
               margin-bottom:5px;
               border-bottom: solid thin;
  }

 .breadcrumbs-spacer {}

 .directory { margin-bottom:5px;clear:left; padding: 5px;}

 .file      { margin-bottom:5px;clear:left;padding: 5px;}

 .thumbnail { display:block;width:100px;float:left;}

 .file ul   { float:left;}

"image" page

 body {
        background-color: #ffffff;
        margin:0;
      }

 .breadcrumbs {
   display:block;
   background-color: #f5f5dc;
   padding:5px;
   margin-bottom:5px;
   border-bottom: solid thin;
 }

 .breadcrumbs-spacer {}

 .directory {
   padding: 5px;
 }

 .file {
   padding: 5px;
 }

 .menu {
        margin-bottom:5px;
        padding:5px;
 }

 .menu-link-previous {
                padding-right : 10px;
 }

 .menu-link-previous img {
                margin-right:15px;
 }

 .menu-link-index {
                 font-weight:600;
 }

 .menu-link-next {
                padding-left : 10px;
 }

 .menu-link-next img {
                margin-left:15px;
 }

 .content {
        padding-top:20px;
      }

 .image { 
        position:absolute;
        top:auto;
        right:auto;
        left:170px;
        bottom:auto;
 }

 .meta { 
        min-width:150px;
        max-width:150px;
        margin:5px;
 }  

 .links {
        border: solid thin;
        margin-bottom: 5px;
 }

 .links span {
        display:block;
        padding:3px;
 }

 .iptc { 
        background-color : #fffff0;
        border-top: solid thin; 
        border-left: solid thin;
        border-right: solid thin;
        margin-bottom : 5px;
      }

 .iptc span { 
        display:block; 
        padding:3px;
        border-bottom:solid thin;
 }

 .iptc-field { 
        background-color : #f5f5dc;
        color:#a52a2a;
        border-bottom:solid thin #000;
        }

 .exif { 
        background-color : #f5f5dc;
        border-top: solid thin; 
        border-left: solid thin; 
        border-right: solid thin; 
        margin-bottom : 5px; 
        }

 .exif span { 
        display:block; 
        padding:3px;
        border-bottom:solid thin;
 }

 .exif-field { 
        color:#a52a2a;
        background-color:#cccc99;
        border-bottom:solid thin #000;
        }

VERSION

0.22

AUTHOR

Aaron Straup Cope

DATE

September 02, 2002

TO DO

  • Teach Apache::Image::Shoehorn how to deal with 'default' images, as described above.

  • Add an "import_styles" method, to take advantage of @import hack for hiding CSS from old browsers. Might just add {import=>1} option to "set_styles".

  • Figure out why I keep getting errors when I try passing STYLESHEET::DATA (or copies of it) to the XSLT munger.

  • Set/get config options using closures.

  • Add hooks to read a conf file - this allow involves hacking Apache::Image::Shoehorn so that it can also read the same conf file

  • Add hooks for generating slides from a "virtual" directory; specifically a list of disparate files.

  • Add hooks for supporting XML::Filter::Sort

  • Consider interactive option that would prompt user for IPTC data as files are being processed.

  • Design and implement nightmarish XPath to generate XSLT stylesheet from a user-defined template. I promised Karl I would do this for v 0.3 but we'll see...

BACKGROUND

http://aaronland.net/weblog/archive/3940

http://aaronland.net/weblog/archive/4474

http://www.la-grange.net/2002/07/22.html

EXAMPLE

http://perl.aaronland.info/image/shoehorn/gallery/www/example/index.html

REQUIREMENTS

XML::Filter::XML_Directory_2XHTML

XML::Filter::XSLT

XML::SAX::Machines

XML::SAX::Writer

Image::Shoehorn

Image::IPTCInfo

Image::Info

Digest::MD5

BUGS

Undoubtedly. So far, it works for me.

LICENSE

Copyright (c) 2002, Aaron Straup Cope. All Rights Reserved.

This is free software, you may use it and distribute it under the same terms as Perl itself.