ImageMaker UI (Java Sample)

This chunk of code is part of my ImageMaker application. It was conceived as a very simple image manipulation program that automates a few tasks required by a stock photography website, like overlaying logos and generating thumbnails. In its current incarnation, it can be run from the command line through a crontabbed script. In the future, I plan on adding a GUI so folks can easily configure and use it manually.

For the meat of the code, check out the ImageHandler Class.


package com.robzazueta.imagemaker;

//import com.robzazueta.utils.Logger;
import java.io.File;
import java.awt.Font;

/*
 *Developer's notes:
 *
 *05/31/01 v.0.7 complete
 *
 *Technically, I should have up-versioned when I finished all the overlay stuff, but I just added text overlay
 *to the ImageHandler class and used it in the ImageMaker class as a preview-only function. If the variable
 *isPreview is set to true, the preview text will be written over every image produced. This will be disabled
 *in officially licensed versions, assuming I want to go that route. Didn't think of this as salable software
 *until yesterday when I realized there may be a serious market for a simple utility like this. I wouldn't
 *charge much more than $15, if that, especially in its current revision, and I'm not sure whether I want to
 *do the whole "preview version/official version" thing or just release it as straight "honor system" shareware.
 *
 *Hell, it may even be useful to release the source. That way, other folks can extend and maintain it along 
 *with me. But, well, we'll see.
 *
 *
 *05/30/02 v.0.6 complete
 *
 *Overlay capability MOSTLY added. Still need to handle alpha transparency. Also added command line parameter
 *handling for where the overlay should be placed.
 *
 *NEW NOTE: Alpha transparency added!!!
 *
 *05/29/02 v0.5 complete
 *
 *So far, it handles taking a directory on a command line, identifying and scaling all images found in 
 *that directory, and outputting to a given directory.
 *
 *TODO:
 *  -   Test it completely: will it overwrite files? What if I don't give it an output directory? What if I don't
 *      specify both X and Y constraints?
 *  -   Make it handle File Not Found errors more gracefully so it can easily recover.
 *  -   Add logging
 *  -   COMPLETE IN 0.6 -- Add overlay capability
 *  -   COMPLETE IN 0.6 -- Add alpha transparency to overlay capability
 *  -   It's probably a good idea to seperate out all of the functionality in this class in order to abstract it
 *      so that I can easily write a GUI for it as well.
 *
 *05/28/02 Began development
 */

/**
 *ImageMaker
 *(C) Copyright 2002 by Rob Zazuedta
 *
 *ImageMaker provides the interface for the image handling classes. It allows the application
 *to be run from either the command line (in the case of crontabbed control) or from a GUI
 *interface. 
 */
public class ImageMaker {
    
    private static void displayHelp() {
        System.out.println("ImageMaker (C)Copyright 2002 by Rob Zazueta\n");
        System.out.println("Usage: java -jar ImageMaker.jar -[ioxyOaedlwh][:attribute:]+");
        System.out.println("Command line attributes:");
        System.out.println("\t-iPath = input directory");
        System.out.println("\t-oPath = output directory");
        System.out.println("\t-xInt = maximum width of output images (Optional if -y defined)");
        System.out.println("\t-yInt = maximum height of output images (Optional if -x defined)");
        System.out.println("\t-OFilepath = path to image to overlay on each pic (Optional)");
        System.out.println("\t-pAlignment = Alignment of overlay (\"top_left\", \"top_right\", \"center\", \"bottom_left\", \"bottom_right\") (Optional)");
        System.out.println("\t-aInt = Percentage to blend overlay (Required if -O is defined, otherwise unused)");
        System.out.println("\t-eString = string to prepend to output files (Optional if -o defined)");
        System.out.println("\t-m = Create output directory if it doesn't exist");
        System.out.println("\t-lPath = location of log the program should write to.");
        //System.out.println("\t-w = open GUI window."); //not supported in v0.1
        System.out.println("");
        System.out.println("\t-h = shows this list");
        System.exit(1);
    }
    
    private static void generateError(String message) {
        String errMsg = "One or more errors occurred:\n";
        errMsg += "\t" + message + "\n";
        System.out.println(errMsg);
        displayHelp();
    }
    
    private static int getAlignment(String align) {
        if(align.toLowerCase().equals("top_left")) {
            return ImageHandler.OVERLAY_TOP_LEFT;
        } else if(align.toLowerCase().equals("top_right")) {
            return ImageHandler.OVERLAY_TOP_RIGHT;
        } else if(align.toLowerCase().equals("center")) {
            return ImageHandler.OVERLAY_CENTER;
        } else if(align.toLowerCase().equals("bottom_left")) {
            return ImageHandler.OVERLAY_BOTTOM_LEFT;
        } else if(align.toLowerCase().equals("bottom_right")) {
            return ImageHandler.OVERLAY_BOTTOM_RIGHT;
        } else {
            generateError("Inavlid alignment: " + align);
            return 0;
        }
    }
    
    
    public static void main(String[] args) {
        //Command line attributes
        boolean isPreview = false; //set to true for the preview copy, false for shipping copy.
        
        String inFile = null;
        String outFile = null;
        int maxX = 0;
        int maxY = 0;
        String overlay = null;
        float alpha = 1.0F; //preset to a default to save myself from writing another if-then
        String prepend = null;
        boolean createDir = false;
        String logPath = null;
        boolean help = false;
        //Logger thisLog;
        File indir = null;
        File outdir = null;
        String[] inArray = null;
        ImageHandler ih = null;
        String ext = null;
        String type = null;
        boolean isImage = false;
        boolean useOverlay = false;
        int align = ImageHandler.OVERLAY_TOP_LEFT; //defaults to the top left of the image
        Font overFont = null;
        String previewString = "PREVIEW PREVIEW PREVIEW PREVIEW PREVIEW";
        
         for (int n = 0; n < args.length; n++) {
            if(args[n].charAt(0) == '-') {
                switch(args[n].charAt(1)) {
                    case 'h'    :   displayHelp();
                    case 'i'    :   inFile = args[n].substring(2); break;      
                    case 'o'    :   outFile = args[n].substring(2); break;
                    case 'x'    :   maxX = Integer.parseInt(args[n].substring(2)); break;
                    case 'y'    :   maxY = Integer.parseInt(args[n].substring(2)); break;
                    case 'O'    :   overlay = args[n].substring(2); break;
                    case 'p'    :   align = getAlignment(args[n].substring(2)); break;
                    case 'a'    :   alpha = Float.parseFloat(args[n].substring(2)) / 100.0F; break;
                    case 'e'    :   prepend = args[n].substring(2); break;
                    case 'm'    :   createDir = true; break;
                    case 'l'    :   logPath = args[n].substring(2); break;
                    //default     :   System.out.println("help is true\n"); help = true; break;
                }
            }
         }
        
         if((args.length <= 0) || help) {
            displayHelp();
            System.exit(0);
        }
        
        //******** Now do all the crazy validation mojo here *********//
        if((inFile == null) || (inFile.length() <= 0)) {
            generateError("No input file specified.");
        }
        
        if((outFile == null) || (outFile.length() <= 0)) {
            outFile = inFile;
        }
        
        if((prepend != null) && (prepend.length() > 0)) {
            outFile = prepend + outFile;
        }
        /* Not in v 0.5
         *TODO -- Add logging
        //Set up the Logger instance for the rest of the application
        if(logPath != null) {
            thisLog = Logger.getInstance();
            //Get a output stream or writer and set the Logger's stream to that
            //Logger.setOutputStream(whatever);
        }
        */
        //******** Start the file manipulation ********//
        
        indir = new File(inFile);
        outdir = new File(outFile);
        
        //Basic directory existence checks
        if(!indir.exists() || !indir.isDirectory()) {
            generateError(inFile + " is not a valid directory.");
        }
        
        if(outdir.exists() && !outdir.isDirectory()) {
            generateError(outFile + " is not a valid directory.");
        }
        
        //If the -m parameter was given, create the directory if it doesn't exist. Otherwise, err out.
        if(!outdir.exists() && createDir) {
            if(!outdir.mkdir()) {
                generateError("Unable to create directory: " + outFile);
            }
        } else if(!outdir.exists()){
            generateError(outFile + " either does not exist or is an invalid directory.");
        }
        
        //If -O was given, make sure the image for the overlay exists
        if(overlay != null) {
            File overFile = new File(overlay);
            if(overFile.exists()) {
                useOverlay = true;
            } else {
                generateError("The overlay image " + overlay +" could not be found.");
            }
        }
        
        //If this is a preview version, select a font to use to write over the images
        //The FontMetric class is abstract... check the Java2D guide to see how to do this the new way.
        //I'll use this in the preview version
        if(isPreview) {
            java.awt.GraphicsEnvironment ge = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
            Font[] availFonts = ge.getAllFonts();
            for(int x = 0; x < availFonts.length; x++) {
                if(availFonts[x].getFamily().toLowerCase().equals("arial") || availFonts[x].getFamily().toLowerCase().equals("helvetica") || availFonts[x].getFamily().toLowerCase().equals("courier") || availFonts[x].getFamily().toLowerCase().indexOf("times") >= 0) {
                    overFont = availFonts[x].deriveFont(50.0F);
                    //System.out.println("Chose " + overFont.getName() + " as the font");
                    break;
                }
            }
        }
        
        inArray = indir.list();
        ih = new ImageHandler();
        
        //Here's where the magic happens
        String tempName;
        String tempOut;
        for(int n = 0; n < inArray.length; n++) {
            tempName = indir.getAbsolutePath() + File.separatorChar + inArray[n];
            //System.out.println(tempName);
            //First, determine the input type. We want to use this as the output type as well.
            if((tempName.toLowerCase().indexOf(".jpg") > 0) || (tempName.toLowerCase().indexOf(".jpe") > 0)) {
                type = "JPEG";
                isImage = true;
            }
            /*  //Due to the Unisys patent (I assume) there is no GIF codec. @!#$@ patent law.
                else if(tempName.toLowerCase().indexOf(".gif") > 0) {
                type = "GIF";
                isImage = true;
            }
            */
            
            if(isImage) {
                //TODO -- add more graceful FileNotFound handling
                ih.loadImage(tempName);
                
                //IF this is a preview copy
                if(isPreview) {
                    if(overFont != null) {
                        ih.overlayText(previewString, 0.75F, ImageHandler.OVERLAY_CENTER, overFont, java.awt.Color.red);
                    } else {
                        generateError("There was a problem generating the images. Please contact the developer with error code F101.");
                    }
                }

                //Check to see if we need to do any scaling
                if(maxX > 0 && maxY <= 0) {
                    ih.scaleX(maxX);
                } else if(maxX <=0 && maxY > 0) {
                    ih.scaleY(maxY);
                } else if(maxX > 0 && maxY > 0) {
                    ih.scaleToFit(maxX, maxY);
                }
                
                //If the overfile is provided and works, use it
                if(useOverlay) {
                    ih.overlay(overlay, alpha, align);
                }
                
                //If we set a prepend for the filename, use it
                if(prepend != null) {
                    tempOut = prepend + inArray[n];
                } else {
                    tempOut = inArray[n];
                }
                ih.saveImage(outdir.getAbsolutePath() + File.separatorChar + tempOut, type);
                isImage = false;
            }
                
        }
        ih = null;  
    }
	

    
}



Home · Photography · Brew Blog · Resume · Contact me
 
Creative Commons License
Unless otherwise noted, all content on this site is Copyright © 2004 by Rob Zazueta and licensed under a Creative Commons License.