Sample scripts

This section will show examples on how scripts can enhance your album in really cool ways. The examples here makes use of the huge library of ready-made java classes that Sun has provided for free and are directly accessible. To better understand the examples and to assist in the writing of your own scripts we strongly recommend that you first read through skin documentation, then bookmark the jAlbum API and Java API from Sun and use them to look up classes and their methods. If you are new to Java it's recommended that you look at these tutorials first and that you then concentrate on the "java.lang", "java.util" and "" packages. They are the ones that are most commonly used. Remenber that you can turn to the forum to get help and to help others!

If all of the code is not visible just select the whole of the code and copy it, then paste it into a text editor to see the missing bits

See Code Snippets for inspiration or help with your coding.


Adding voice annotations

Many digital cameras allow you to add voice annotations to images. The camera usually puts a wav file next to the image bearing the same base name as the image. This script will make JAlbum look for these wav files and insert a BGSOUND tag if there is an annotation. Put the script just after the body tag of a slide.htt file.

<!-- add and play voice annotations (.wav files) if they exist -->
  import se.datadosen.util.IO;
  File sound = new File(imageDirectory, label+".WAV");
  if (sound.exists()) {
    // Make a copy if needed
    String soundPath;
    if (!outputDirectory.equals(imageDirectory) && engine.isCopyOriginals()) {
      IO.copyFile(sound.getAbsolutePath(), outputDirectory, true);
      soundPath = "../" + sound.getName();
    else soundPath = IO.relativePath(sound, new File(outputDirectory, "slides"));
    out.println("<BGSOUND SRC=\"" + soundPath + "\">");

Counting objects

You can write code to first collect counters for all categories in one count operation and then obtain the number of items for each category by using the count categories utility. The categories you can get counts for are audio, image, video, webPage, webLocation, folder and other

//Ways of getting the counters

//Counters for the current folder
CategoryCounters counters = JAlbumUtilities.countCategories(currentFolder, false);

//Counters for the root folder 
CategoryCounters counters = JAlbumUtilities.countCategories(rootFolder, false);

//Counters for the whole project, true uses recursion to count subfolder contents
CategoryCounters counters = JAlbumUtilities.countCategories(rootFolder, true);

//Examples for get the counts
Integer imageCount = counters.getCount(Category.image);
Integer webPageCount = counters.getCount(Category.webPage);

Exclude from index pages folders that are empty or have only empty subfolders

This can prove useful if you start a project by making the folder structure first, and then in subsequent builds add files to those folders. Instead of having links to empty folders the album will only show thumbnails for folders with some content, even if it is in a further subfolder. Note, the empty folder and its contents are still made, it is just that they do not appear on index pages with content.

Example is based on a version of Minimal skin's index page thumbnail generation . Within the iterator we need to check if the current file is a folder or not. If it is then we need to see how many non-folder files are included in it and all of its subfolders. We set a boolean to true, so the default is to add thumbnails as normal, but we set that variable to false if a folder is empty or has only empty subfolders.

<%-- Iterate through images and produce an index table --%>
<div id="thumbnails">
    boolean hasFiles = true;
    if(currentFile.isDirectory()) {
      //Get the counters for this folder
      CategoryCounters counters = JAlbumUtilities.countCategories(currentObject, true);
      //Is the file count of this and all its subfolders 0? If yes, don't include it
      if(counters.getFileCount() == 0) hasFiles = false;
  <ja:if test="${hasFiles}">
    <a href="${closeupPath}">
      <ja:if exists="iconPath">
      <img class="noborder" src="${iconPath}" width="${thumbWidth}" height="${thumbHeight}" alt="${title}" title="${title}">
      <img src="${thumbPath}" width="${thumbWidth}" height="${thumbHeight}" alt="${title}" title="${title}">
      <%= getLabel(currentObject) %>

Extra slide pages for original images

When clicking on an image in a slide show, you may get to the original image, but it is not displayed in a html page of its own. The downside of this is that the surroundings for the image doesn't match the skin (usually displayed on white background) and you have to use the back navigation button to return. To address this issue duplicate your skin's 'slide.htt' file and rename it 'originalslide.htt'. Now, make the following simple adjustment to the "slide.htt" file and it will make sure that the "originalslide.htt" file gets processed if needed.

<%-- Image, maybe with link to original --%>
<ja:if exists="originalPath">
    <%-- Create a slide page for the original image too and link to that one  --%>
    <%-- instead of linking to an image --%>
        String originalPage = originalPath;  // Default if no extra template page
        File template = new File(skinDirectory, "originalslide.htt");
        if (template.exists()) {
            originalPage = label+"_orig"+engine.getPageExtension();
            new File(outputDirectory,"slides/"+originalPage));
    <a href="<%=originalPage%>">
        <img src="${imagePath}" />

Modify 'originalslide.htt' as required, such as the image src path so it points to the original instead of the slide image.

Generating extra slide images

If you want to offer additional sized images, such as for HTML’s srcset then this example code shows you how. It will generate extra images with image bounds of 400px, the extra images also have any filters applied as would the slide image.

In slide.htt:

ai = new AlbumImage(currentObject);
ai = ai.scaleToFit(new Dimension(400,400));
ai = ai.applyFilters(JAFilter.CLOSEUPS_POSTSCALE_STAGE);
String name = label + "_medium.jpg";
File dest = new File(outputDirectory, engine.getSlideDirectory() + "/" + name);
String mediumPath = IO.urlEncode(name);

Later in slide.htt, to display the new image:

<img src="${mediumPath}">

Getting metadata

File metadata can be accessed from the ‘meta’ object, use jAlbum’s list metadata to ensure both that the metadata exists and that the correct tag name is used. For example, to use an image’s IPTC Byline data use:

<%= meta.get("Iptc.Byline") %>

Multilevel (breadcrumb) parent links

You might have an album with many nested folders (animals/mamals/cats...). In this case it helps a lot to have all folder names displayed like this: animals » mamals » cats (being in the "cats" folder), with links to each parent folder. Copy and paste the scriptlet below for this effect:

Note that the start tag has an exclamation mark after the percent symbol, this ensures the scripted method is only evaluated once making it faster and more memory efficient.

  void makeBreadcrumbs(AlbumObject folder, String prefix) {
    if (folder == null) {
    makeBreadcrumbs(folder.getParent(), "../");
    String title = folder.getTitle();
    if ("".equals(title)) title = folder.getName();
    out.println("<a href=\"" + prefix + firstIndexPage + "\">" + title + "</a> » ");

  void makeBreadcrumbs() {
    makeBreadcrumbs(currentFolder, "");

<% makeBreadcrumbs()%> <%-- sample call from index.htt --%>

<% makeBreadcrumbs(currentFolder, "../")%> <%-- sample call from slide.htt --%>

Reading captions/comments from separate text files

Some people wonder if it is possible to have jAlbum insert the contents of a text file having the same base name as an image but with ".txt" extension, for example "hiking.jpg" will get text from "hiking.txt". This is simple to do with the following scriptlet:

<!-- Extract text from textfiles carrying the same base name as this image -->
<ja:include page="<%= new File(imageDirectory, label+".txt") %>" />

Saving and getting object custom variables

Within an iterator of an index page (parsed before slides) you have access to the "current" variable, which is a Map object where you can store image specific variables and later retrieve them in the slides. Store like this:

current.put("variableName", variableData);

and get this data back for printing on a slide page like this

 <%= current.get("variableName") %>

Theme images

Skins can easily create theme images for any folder, see explore/folder properties pane. By default theme image support is disabled, to enable it you need to provide a string variable called ‘folderImageSize’ with width and height dimension i.e. 1000x300 would be an image 1000px wide and 300px high.

Note, if you want to use an alternative variable name to folderImageSize you can do so by adding themeImageSizeName=yourtFolderImageSizeVariableName to the file

The folderImageSize variable can be added to the SkinModel file as

public String folderImageSize = “1000x300”;

Or in onload.bsh as

public JTextField folderImageSize = new JTextField("1000x300");

Also by default the theme image will be based on the image selected for the folder thumbnail. If you want your skin to support separate thumbnail and theme images in the file add


To produce the theme images there are three methods, let jAlbum make the theme images for you or take control and produce them yourself.

Note: Examples below show one possible method of adding and styling the theme image to the index.htt template, you can of course add them however you want.

Method 1: Built in routine Uses a skin property element that controls the editable status of the theme image size and cropping from a contextual menu. To allow access to this menu in the file add

separateThemeImage=true  //or false

In init.bsh add

engine.setThemeImageProcessor(new ThemeImageProcessor());

In your skin's index.htt, where you want to include the theme image add, for example as a CSS background image for a class of 'themeImage'

<ja:if exists="themePath">
  .themeImage {
     background: url(${themePath}) center top no-repeat;

Method 2: jAlbum control: To init.bsh and

engine.setThemeImageProcessor(new ThemeImageProcessor());

In your skin's index.htt, where you want to include the theme image add, for example as a CSS background image for a class of 'themeImage'

<ja:if exists="themePath">
  .themeImage {
     background: url(${themePath}) center top no-repeat;

If you want other names or locations for the generated theme images you can create a subclass of the ThemeImageProcessor. In the example below the theme image is called ja_theme.jpg and it is put in the slides directory instead of the root of the current directory.

ThemeImageProcessor custom = new ThemeImageProcessor() {
  public String getThemePath(AlbumObject folder) {
    return engine.getSlideDirectory() + "/ja_theme.jpg";

Note:There are three variables produced by jAlbum ${themePath}, ${themeWidth} and ${themeHeight}

Method 3: skin control: in postdir.bsh add

//Import required resources
import java.awt.Dimension;
import java.util.Map;
import java.util.logging.Level;
import net.jalbum.filters.CropFilter;
import se.datadosen.jalbum.AlbumImage;
import se.datadosen.jalbum.AlbumObject;
import se.datadosen.jalbum.EmbeddedProperties;
import se.datadosen.jalbum.JAlbum;
import se.datadosen.jalbum.JAlbumUtilities;

 //Get the theme image dimensions, jAlbum uses themeImageSizeName variable to get the dimensions
Dimension themeDim = engine.getThemeImageDim();
//Create an album object for the current folder’s theme image
AlbumObject theme = JAlbumUtilities.getThemeObject(currentFolder);
//Ensure dimension are set and an image object exists to work on
if (themeDim != null && theme != null) {
    File themeFile = new File(outputDirectory, "ja_theme.jpg");
    //Use builtin routines to compare the image to be used and the image generated, create/update the generated image only if required
    //for example crop focus changes or a different image selected
    EmbeddedProperties existing = new EmbeddedProperties(themeFile);
    EmbeddedProperties current = new EmbeddedProperties();
    current.put("themeSrc", theme.getPathFrom(currentFolder));
    current.put("themeDim", themeDim.width + "x" + themeDim.height);
    if (!engine.isAppendImages() || JAlbumUtilities.isDirty(themeFile, theme) || !existing.equals(current)) {
    //log information to the console, only visible when logging level is set to FINE in preferences/general
        JAlbum.logger.log(Level.FINE, "Regenerating theme image ja_theme.jpg (" + themeDim.width + "x" + themeDim.height + ") for folder {0} ", currentFolder);
        //Create the theme image using the dimension set and cropped as shown in the folder properties pane
        AlbumImage ai = new AlbumImage(theme);
        CropFilter cf = new CropFilter();
        double ratio = (double) themeDim.width / themeDim.height;
        int width = ai.getBufferedImage().getWidth();
        int height = ai.getBufferedImage().getHeight();
        Dimension bounds = new Dimension(width, (int) (width / ratio));
        if (bounds.height > height) {
                bounds = new Dimension((int) (height * ratio), height);
        ai = ai.applyFilter(cf, (Map) theme.getVars());
        ai = ai.scaleToFit(themeDim);

You can of course change the location and file name of the theme image by modifying the above code. For more information on the above code see: All that is left to do is include the theme image in your skin's pages, it is recommended you add the theme image as a 'background image'.

Skin properties API




Traversing the project

The standard way of traversing a nested structure such as a jAlbum project is to use recursion, which can seem non intuitive. Since jAlbum 15 an easier to grasp method has been available, the 'get descendants' API call. An example of using this is shown below, it generates a sorted unique list of keywords used throughout the project.

Set keywords = new TreeSet();
for (AlbumObject ao : rootFolder.getDescendants()) {

Other examples are

// Count all but hidden and excluded objects
// Count all but excluded objects

Note, if not using Beanshell/Java you will need to first use

var IncludeOption = Java.type("se.datadosen.jalbum.IncludeOption");

For more information an examples see this forum post by jAlbum's creator - of which the above is an extract of.