On Wed, May 08, 2002 at 01:42:42AM -0700, Michael A Nachbaur wrote:
> This is an request for comments.  If you don't care about dynamic image
> generation with mod_perl, or don't care about offering or reading about
> suggestions, you can safely ignore this.  Also, be forewarned, this was
> written in StarOffice, and then copied/pasted into my email program, and
> hand tweaked, so some things may not have made the transition properly.
> 
> Dynamic Image Manipulator
> -------------------------
> 
> *) Overview
> 
> This is a mod_perl handler, not directly tied in with my content
> management system, but is/will be used extensively by it. The premise is
> to dynamically generate images, cache them, and present them to browser
> clients. The URI, as well as Apache configuration directives, is used to
> determine what is to be generated.
> 
> *) Basic Uses
> 
> The most basic uses of this application will to dynamically generate TTF
> text for titles, buttons, sidebars, etc. The current version of this
> code does this, and quite well. Foreground and background colors, font
> name (with bold/italic support), font size, image size (or automatically
> detected based on the size of the text), and rotation.
> 
> The basic text support could be extended to allow for images to be
> overlayed on the text (or placed under the text), or stretch images
> similarly to how Enlightenment displays window manager themes.
> 
> Other uses planned would be to manipulate existing images. For instance,
> if an image on a website needs a thumbnail, medium size and full-size
> view, normally a person must make all versions by hand. If any
> formatting needs to be done, like borders or drop-shadows, this
> increases complexity. If a person could just drop an image in a
> directory, and link to that image, the image could automatically be
> resized, borders added, drop shadows put in place. The resulting image
> would then be cached, and outputted.
> 
> *) URI Arguments
> 
> Information about what is to be done is passed through the URI. This
> works for simple tasks like text display, but if anything more
> complicated is to be done, external configuration files must be used.
> We'll get to that in a bit.
> 
> Essentially, arguments are passed using the PATH_INFO HTTP header. We
> want the browser to think this is an actual file, instead of a
> dynamically generated image, so that the browser is more inclined to
> cache the content. So, a typical query would be:
> 
>   http://localhost/genText/font=ArialBold;size=24;fgcolor=#ffffff;
>   bgcolor=#000000;rotate=90;text=This+Is+The+Text
> 
> Resizing an image could be accomplished by doing:
> 
>   http://localhost/genImage/source=/images/ducks.jpg;scale-ratio=1:1;
>   width=120;height=80;border-size:1px;border-color:#000000;
>   shadow-color:#000000;shadow-angle:270;shadow-distance:5px
> 
> This would resize an image to the indicated width/height. The
> "scale-ratio" argument would limit the width/height ratio, so the
> maximum dimension would be used. The other attributes are obvious.
> 
> *) Configuration Files
> Lets assume that we are going to scale an image, add borders to it which
> consist of other images, and add text captions over the image. This
> would result in such a long URI, that browsers would probably truncate
> it. In addition, providing direct access to the browser opens up
> possibilities for DoS attacks. Therefore, a configuration file should be
> used. The config file must be flexible enough to allow a web page to
> provide various input, but have certain defaults set, and restricted.
> 
> The proposed solution would be to have a config file that has preset
> default templates that the input arguments augment. For instance:
> 
> <genimage>
>   <preset name="thumbnail-image">
>     <image>
>       <style> 
>         border-style: solid;
>         border-color: #000000;
>         border-width: 1px;
>         shadow-distance: 5px;
>         shadow-angle: 270; <!-- or something like 1.2rad -->
>         shadow-color: #000000;
>       </style>
>       <content name="src"/>
>     </image>
>     <image>
>       <style href="/css/watermark.css"/>
>       <!-- The above-referenced file has the following contents:
>         opacity: 80%;
>         position: top;
>       -->
>       <content>/images/watermark.gif</content>
>     </image>
>     <text>
>       <style>
>         font-face: Arial;
>         font-size: 10px;
>         color: #000000;
>         border-style: solid;
>         border-color: #ffffff;
>         opacity: 80%;
>         text-align: right;
>       </style>
>       <content>Copyright &copy; 2002 Foo Bar Industries</content>
>     </text>
>     <text>
>       <style>
>         font-face: Arial;
>         font-size: 14px;
>         color: #ffffff;
>         text-align: left;
>         position: top;
>       </style>
>       <content name="date"/>
>     </text>
>   </preset>
> </genimage>
> 
> As you can see, the above configuration file uses CSS. It makes sense to
> leverage that, although I'm not certain how difficult it would be to
> interface with CSS files. As far as I know, there are Perl CSS parsers,
> but I have yet to use them. The configuration for a preset config
> template would be layered, so the earlier the definition, the lower the
> layer is. The real important part here, is the "name" attribute of any
> element, as this identifies where input can be indicated. The above
> preset could be used by invoking the following URI.


I used CSS.pm for a bit but it was too fat w/ Parse::RecDecent. To unify my
app and the browser I use axkit to 'generate' the css from an xml file.

<css>
 <selector name="back">
    <color>black</color>
    <font-family>geneva</font-family>
    <font-family>arial</font-family>
    <font-size>7px</font-size>
    <background-color>white</background-color>
 </selector>
</css>

.back {
    color: black;
    font-family: geneva, arial;
    font-size: 7px;
    background-color: white;
}

Creating complicated css files are difficult but my drawing app can load its
info from the uri, an xml file, a rdbm , Config::General, inifiles or whatever
and use different output methods such as axkit's providers to parse the 
'color config' to render the *.css file to the browser.

This way the document, style, skin and images are all unified.

Graphics::ColorNames works wonderfully to help handle all the different color
needs.

> 
>   http://localhost/genImage/preset=thumbnail-image;src=/images/ducks.jpg
> 
> As you can see, the preset is invoked by passing it's name as an
> attribute, and any element that has a name attribute, it's value can be
> provided on the URI. If an element has both a value and a name
> attribute, the value in the config file can be used as a default.
> 
> *) Caching Schemes
> 
> A caching scheme similar to AxKit could be used. The current module
> takes all the input arguments, sorts them (including all values that are
> not provided, for completeness), and takes it's MD5 checksum. That
> becomes the image's filename on the system. It is placed in a temporary
> directory, and any further requests to that same URI, the file is pulled
> from the filesystem without regenerating the image. Further, the code
> has been blatantly ripped off from AxKit, which separates the directory
> into two sub-levels, to prevent performance problems of having too many
> files in one directory.
> 
> Note: To prevent the filesystem from filling up, due to DoS attacks, it
> may be prudent to have a cron job periodically cull files that have the
> oldest access time.


Cache::Cache is appropriate here ... 



> 
> *) Image Manipulation Modules
> 
> My current code uses GD for text writing, and I'm quite happy with it.
> It is extremely fast, and creates nice text output when compiled with a
> TTF font engine. Looking forward however, it may not be as desirable if
> things like drop shadows is to be done. GD can work with multiple
> images, can resize them, etc, but the advanced features are still
> unknown.
> 
> *) File Expiration Headers and Browser Caching
> 
> With my current code, it seems that browsers are reluctant to cache
> these dynamically generated images. I have passed Expires: headers to
> tell the browser to cache file file for a long period of time (2+
> weeks), but I have been unsuccessful. I know the caching headers are
> complex, and needs more than one simple header, but fixing this has
> moved to the back-burner of my project. However, if more complicated
> processing is to be done, and with more images, it will be crucial to
> make browsers cache these images.


I create a digest w/ MD5 or SHA1 for the image/pdf and use it as the filename
and the Cache::Cache key.

The cache is easily invalidated if the source image file failes a -e test.
I also use cron to delete stale image files.

The generated now static image is redirected-to or referenced in the html.  
I found that it is important to complete the  processing of the images before 
the referencing html document gets served, ... rather than having the html 
document initiate dynamic imbeded links to create the image.  Letting apache 
serve images as static image-files has proved rock solid for me 
... (note keep-alives).  There is nothing worse than to pull a page and have
to wait for each of the images to show up.

Browsers, proxies and users are all real pains to deal w/ when the uri has
a query string.  Digest's are ugly but they play much better w/ everybody.

014d1c89fc3da6e15e0069000dfa381e44239af71021057594.png

Ed

Reply via email to