A-Gen - an album generator


Table of Contents

Overview
Requirements
Building and installing the software from source
Usage
Building own templates
Template structure
The template language
Example template

Overview

A-Gen is a web album generator tool that generates static web pages from templates. Templates are just like normal HTML-files with some minor markup for the template processor. The program takes a template and a set of pictures and outputs a directory containing a web album consisting of static HTML pages. The album can then be uploaded on any type of webspace. There is no scripting part on the web space necessary, all content creation is done offline. Due to this mode of operation this software is intended for use with albums that are unlikely to change afterwards (e.g. pictures from a journey or a party). If you need to change your album on a regular base (perhaps like some kind of photo blog or things like that) then A-Gen is not the right thing for you. While you certainly could regenerate the album each time you add a picture and then upload the files that differ from the last run, you should better go for some dynamic solution with a web interface for album management. On the other side, if you want to create albums that don't change and can be used everywhere, independent of a webserver or a special software (ok, a webbrowser would be nice though...), then A-Gen might be the right tool for you (it is for me).

Requirements

  • A POSIX-compatible system (A-Gen is developed on a Debian GNU/Linux unstable but should compile an nearly any modern UNIX(TM)

  • The Boost Filesystem Library. This library provides means to portably represent and manipulate paths in A-Gen.

  • ImageMagick, a toolset to easily manipulate images from the command line. A-Gen uses the convert command to create thumbnails for the albums.

  • (Build time only) The SCons build system This software is used to build A-Gen.

  • (Build time only) A C++ compiler. As A-Gen is written in C++, you'll need a working compiler to build the software. Develepment is done using the GCC 3.3, but any modern compiler that is able to compile Boost should suffice.

Building and installing the software from source

  1. Get the source from http://a-gen.dev.slash-me.net

  2. Unpack the source and change into the source directory.

  3. Do a scons PREFIX=<prefix> to compile the software (if PREFIX is left blank, the software will be compiled for installation in /usr/local)

  4. To install the software, use scons PREFIX=<prefix> install (PREFIX is the installation prefix, /usr/local is default)

Usage

The general syntax for generating an album is: a-gen [options] <pictures>

[options] can be one or more of the following:

  • -f <file> Use <file> as the title file. A title file contains one line per picture with the form: <filename> | <image title> Filenames may occur with path information. (not every image in the album must be listed in that file. Images not listed will get an empty title.)

  • -h Prints help and exits

  • -o <path> Use <path> as target path for the album. The whole album structure will be created beneath this directory. If it does not exist it will be created.

  • -s <suffix> Use <suffix> as the suffix for the HTML-files. Default is .html

  • -S <size> Generate thumbnails with <size> pixels on the long side. Default is 120px.

  • -t <path> Use <path> as the albums template.

  • -T <title> Use <title> as the album title.

  • -v Be verbose when creating the album.

  • -V Print the program version and exit.

Building own templates

This section describes how to build own templates that can be interpreted by the album generator.

Template structure

A template is a directory that contains (in the most basic case) two files: album.tem, the template for the albums index file and picture.tem, the template for the picture pages. Every other file or directory that is contained inside the template directory is considered to be a auxiliary object and is copied directly to the output directory. That enables the template designer to use picture etc. as buttons and have them automatically copied to the resulting album. Please note that all auxiliary objects are to be referenced with a relative path inside the template files that starts on the same level where the template is located. E.g. if the template directory contains a subdirectory buttons which in turn contains a file index.png. The button would be referenced like this inside a template:

<img src="buttons/index.png" alt="Index" />

Note

Please do not use a subdirectory named pics inside the template. Doing so will under some circumstances result in auxiliary objects being overwritten by pictures. Just consider this name as reserved.

The template language

The template language is a collection of tags that are embedded inside a normal HTML-file.
        These tags control the interpretation of the template by the template processor.

Overview/Commands

The template language merely consists of some special tags enclosed in <{ }>. The language was designed to contain just the tags necessary to express some simple logic for the template processor. While at first it seemed enough to do some variable replacement it came appearant that a more sophisticated interpreter allowing for conditional execution and loop constructions would greatly simplify the creation of templates.

The template processor has knowledge of the following commands:

  • ALBUM This command returns a reference to the album object. The command must never be used alone. Rather it shall always be used in the form ALBUM.Field where Field is a specific field of the ALBUM object. See The ALBUM object for further details.

  • ECHO x The ECHO command prints the text given as parameter to standard error on execution time. No text is written to the output file. This command is merely intended for printing copyright messages or debugging output.

  • IF test/ENDIF The IF command is used to conditionally include or exclude the contained code in the output. If test resolves to true, the code between IF and ENDIF will be included in the output. See Tests for details on writing tests.

    Example 1. An example using IF/ENDIF

    <{IF DEFINED("NEXT")}>
      <a href="<{IMAGE(NEXT).Link}>Next Image</a>
    <{ENDIF}>
                      

    The HTML-link in the example will only be included in the output when the next image exists (i.e. the magic variable NEXT is defined by the system)

  • IMAGE(n) The IMAGE(n) command returns the n-th image of the album (in the order given on the command line). The command must not be used alone, but always in the form IMAGE(n).Field where Field references one of the properties of the IMAGE object. See The IMAGE object for further details.

  • INCLUDE x This command adds the contents of file x to the output. The included file is NOT interpreted, so the template commands make no sense in there. This may be useful if you want to create a template with some information that is always changing from run to run. You may put that information into a file which you include via this command without modifying the template itself.

  • REM x The parameters of this command are simply ignored. It is intended to be used for comments in the template source.

  • WHILE test/ENDWHILE These two commands are used for constructing loops to repeat some code fragment over and over again. As long as test is true the code inside the loop will be interpreted and included in the output. See Tests for further informations about writing tests and the Example template for examples on using these commands.

The ALBUM object

The ALBUM object contains all information about the album itself. Things like the album title, creation date and time can be accessed via this object. Fields of the ALBUM object are accessed this way: ALBUM.Fieldname. The object has the following fields:

  • ALBUM.Date The album's creation date. Set by the interpreter at initialization.

  • ALBUM.Index A link to the album's index file (default: index.html)

  • ALBUM.Time The album's creation time. Set by the interpreter at initialization.

  • ALBUM.Title The album's title as given on the command line.

  • ALBUM.Thumb_Size The album's thumbnail size.

The IMAGE object

The IMAGE object actually is an array of all images in the album. The images are accessed by an index number. There are two special numbers defined by the system for use inside the templates: FIRST and LAST denoting the index of the first and last picture in the album. Inside the picture.tem template there are three nother special numbers defined: CURRENT, NEXT and PREV. These numbers point to the predecessor, itself and the successor of the current image. IMAGE objects contain several fields with informations about the images. Whenever you access the IMAGE object, you must access a field. This is done in the following way: IMAGE(number).Fieldname.

The following fields are defined for the IMAGE object:

  • IMAGE(n).Link The URL to the image's page inside the album. This will contain the HTML-file that contains the image.

  • IMAGE(n).Name The filename of the image file.

  • IMAGE(n).Url The URL to the image file inside the album. This will point to the image file that is contained in the pics/ subdirectory of the album.

  • IMAGE(n).Size The size (in bytes) of the image file.

  • IMAGE(n).Title The title of the image as read from the title file.

  • IMAGE(n).Thumb_Url The URL to the image's thumbnail file inside the album. This will point to the thumbnail that is contained in the pics/thumbs/ subdirectory of the album.

Tests

As already written in the language overview, the development of the language quickly showed the need for conditional execution of certain template parts. One example: imagine you want to show some buttons in the image pages that allow the user to navigate forward and backward through the album (Ok, you wont have to stress your imagination too hard, as the example template does exactly this). But when at the last picture, you don't want to show the "Next"-button. That's where tests kick in. Using a test to check whether there exists a next image, you can hide the "Next"-button in the last image file. You would simply write:

<{IF DEFINED("NEXT")}>
  <a href="<{IMAGE(NEXT).Link}>"><img src="misc/forward.gif" alt="Next"/></a>
<{ENDIF}>            
          

The DEFINED("NEXT") part is called a test. A test is a piece of code that checks for a certain condition and returns true or false. Depending on the results, the command pairs IF/ENDIF and WHILE/ENDWHILE will the parts between them or not. There are a bunch of tests defined suitable for most situations:

  • AND(x, y) This test checks whether both x and y are true. x and y are tests themselves.

  • DEFINED(x) This test checks whether the variable x (where x is given as a string, i.e. enclosed in "") is defined inside the systems variable store. A variable may either be defined by the system itself (e.g. the special variables PREV, NEXT etc.) by assigning a value to it inside the template (see Expressions).

  • EQUAL(x,y) Checks whether the values returned by the two expressions x and y are equal. Comparison is done as strings, numbers are converted first. Therefore EQUAL(1, "1") would result true.

  • GREATER(x,y) Returns true when x is greater then y. This is only useful for numbers so the interpreter will throw an error if x and y can not be converted to numbers.

  • LESSER(x,y) Returns true when x is lesser then y. This is only useful for numbers so the interpreter will throw an error if x and y can not be converted to numbers.

  • NOT(x) Negates the result of test x.

  • OR(x, y) This test checks either x or y or both are true. x and y are tests themselves.

Expressions

Expressions are a flexible way of calculating the input for a command or an assignment on the fly. The assignment is a special type of expression that is written variable = expression. variable is the name of the variable that should hold the expressions value. The interpreter tries to interpret everthing that is not a command as an assignment.

The expression itself is defined to be either just a value (therefore the expressions value would be the value itself), a variable (then the value would be the variable's value) or either one connected via an operator to another expression. One may notice that this definition is some kind of recursive. Resolving all the recursion shows that the expression is a number of variables and values connected by operators.

The different operators are:

  • x + y Adds two numbers. If the values of the two operands can not be converted to numbers, an error is thrown.

  • x - y Substracts two numbers. If the values of the two operands can not be converted to numbers, an error is thrown.

  • x * y Multiplies two numbers. If the values of the two operands can not be converted to numbers, an error is thrown.

  • x / y Divides x by y. If the values of the two operands can not be converted to numbers, an error is thrown. Note that the division is carried out using whole numbers. Therefore if the division can not be done without remainder, the remainder is discarded (e.g.: 3/2 yields 1 instead of 1.5).

  • x concat y Concats the two strings x and y. If either one is no string, it will be converted to a string value. Therefore you can concatenate a string and a number if you like.

The order of the operators is the following: * and / bind stronger then + and -. "concat" has the weakest binding.

The variables used in the expressions must be defined first (i.e. they must have been assigned a value). Otherwise an error will be thrown. A variable name must start with a letter and contain letters, numbers and underscores only. A variable MUST NOT have the same name as a reserved word (all commands are reserved words). Values used in the expressions are either numeric values (whole, positive numbers only) or string values (enclosed in "").

Example template

Below is a full and working example for a template. Indeed this is the testing template that is used
        to develop the parser and interpreter.

The example template consists of the two templates and a subdirectory named misc containing three images: back.gif, forward.gif and index.gif. The template files are interpreted by the template processor resulting in some HTML-files, while the subdirectory misc and it's contents are copied to the output directory.

The file album.tem has the following contents:

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
 <head>
  <title>
   <{ALBUM.Title}>
  </title>
  <style type="text/css">
  <!--
        img {
                border: 0px;
        }
  -->
  </style>
 </head>
 <body style="background-color: #000000; color: #FFFFFF; text-align: center">
  <h1 style="text-align: center"><{ALBUM.Title}></h1>
  <{counter = 0}>
  <table style="margin-left: auto; margin-right: auto; text-align: center">
   <{WHILE LESSER(counter, LAST+1)}>
    <tr>
     <{inner_counter=0}>
     <{WHILE LESSER(inner_counter, 4)}>
      <td>
       <{IF LESSER( counter, LAST+1)}>
        <p style="text-align: center; vertical-align: center">
         <a href="<{IMAGE(counter).Link}>"><img
            src="<{IMAGE(counter).Thumb_Url}>" alt="" /></a>
        </p>
       <{ENDIF}>
       <{counter = counter + 1}>
       <{inner_counter=inner_counter+1}>
      </td>
     <{ENDWHILE}>
    </tr>
   <{ENDWHILE}>
  </table>
  <p style="font-size: small; text-align: right"><{ALBUM.Date}></p>
 </body>
</html>

The picture.tem contains the following code:

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
 <head>
  <title><{IMAGE(CURRENT).Name}></title>
  <style type="text/css">
   <!--
    img {
        border: 0px;
    }
    p {
        color: #FFFFFF;
    }
   -->
  </style>
 </head>
 <body style="background-color: #000000; color: #FFFFF: text-align: center">
  <p style="text-align: center">
    <{IF DEFINED("PREV")}>
     <a href="<{IMAGE(PREV).Link}>"><img src="misc/back.gif" alt="Previous"/></a>
    <{ENDIF}>
    <a href="<{ALBUM.Index}>"><img src="misc/index.gif" alt="Index"
        style="margin-left: 100px; margin-right: 100px" /></a>
    <{IF DEFINED("NEXT")}>
      <a href="<{IMAGE(NEXT).Link}>"><img src="misc/forward.gif" alt="Next"/></a>
    <{ENDIF}>
  </p>
  <p style="text-align: center">
   <img src="<{IMAGE(CURRENT).Url}>" alt="" />
  </p>
  <p style="text-align: center">
  <{IMAGE(CURRENT).Title}>
  </p>
 </body>
</html>