How a Software Engineer Puts Together a Wedding Album

With our wedding three months behind us, the time has arrived to place our wedding album order. I’m not sure how a normal person would go about figuring out which layouts work with which photos, but I can tell the story of how a Software Engineer, myself, went about the process.

Prep Work

When we had met with Timothy Whaley & Associates back in September, we were given:

  • Our proof book (a 12 × 11 inch book with 12 wallet sized photographs on a page)
  • A mat guide (a 8 × 11 inch sheet of paper with 35 little images of what each album page would look like)
  • And, a link to an online site to view and order individual prints.

Stephanie and I sat down together one night and flipped through the proof book, writing down the number of each photograph that we would like to have in our album. The easy part was done. Next we would have to figure out the layout of each photo on a page and each page in the album.

Since the proof book is property of Timothy Whaley & Associates (until we place our order at which point it becomes ours to keep for hitting a certain dollar amount in photo sales), it’s not as simple as cutting out each photo and arranging them on a black sheet of colored paper to mimic the mat layouts, we would end up owing Timothy Whaley & Associates the cost of the album, so the software engineer part of my brain kicks in… okay, so that part is always on, but I digress…

Images I can play with

I wanted images I could manipulate. Something I could throw around, re-arrange, and swap out as I please. I spend most of my day in front of a computer, so I am just as comfortable throwing around digital images as I am with hard copies. This is where the link they gave us come into play.

I logged into our online photo collection (Wedding Photo Proofs), clicked on the first thumbnail to see the bigger version and found the URL for that image:blah/blah/blah/getImage.aspx?fileName=blahblahblah%2f0001.jpg&useProof=True

That URL taught me a few things. First, the images are stored sequentially (note the 0001.jpg). Secondly, the images are stored clean and the getImage.aspx page overlays the word ‘proof’ when displaying the image to a user (note the useProof=true).

Having been using the WWW::Mechanize class in Ruby a lot recently (for HackThisSite which I will talk about on a later day), I wrote a quick little program to download all of my proof from the web site:

require 'rubygems'
require 'mechanize'

agent = WWW::Mechanize.new
url = 'blah/blah/blah/getImage.aspx?filename=blahblahblah%2f'

for num in 1..500 do
  filename = "%04d.jpg" % num
  file = agent.get( url + filename )
  file.save_as( filename )
end

That small block of Ruby code was all I needed to download all 500 of my wedding photo proofs free of the ‘proof’ overlay.

Okay, now I have all of my wedding proofs in a digital format. What next?

Building the Album Pages

So, now I could print out all the images or load them into a photo editor to re-arrange as needed, but that doesn’t satisfy the geek in me.

I installed RMagick on my MacBook and got to work writing another Ruby application to take in a mat type and list of photographs and produce an image of what the finished product would look like. The bulk of the time I spent on the program was getting the top left coordinates of each photo for the page layout. The RMagick code that actually creates the image pretty much boils down to this:

...

dst = Magick::Image.new( 960, 1440 ) { self.background_color = 'black' }

@photos.each do |photo|
  src = Magick::Image.read( photo[:name] ).first
  src.background_color = 'transparent'
  src.crop_resized!( photo[:size].width, photo[:size].height )
  src.rotate!( photo[:rotate] ) unless photo[:rotate] == 0
  dst.composite!( src, photo[:offset].left, photo[:offset].top, Magick::OverCompositeOp )
end

...

You can download the Artist Album Page Builder (album_page.rb) Ruby program. It allows you to build 32 of the 35 mat templates (doesn’t include Half Panoramic, Panoramic, or Cover Cameo). You can build a sample of all the pages by running: ruby album_page.rb --samples

Building samples checks for two files, one called _sample_portrait.jpg and the other called _sample_landscape.jpg, if it doesn’t find those, the program creates it’s own sample images to use in generating the sample mat files.

Putting it all together

With the album_page.rb program I wrote, I was able to create as many combinations of layouts and photos as I liked.

And that is how a Software Engineer puts together a wedding album.

Example (with photos from our Honeymoon in Kauai, Hawaii):

Wheelmobile

Stephanie and I spent part of the weekend standing outside in the cold to get a chance to try out and become a contestant on Wheel of Fortune. Unfortunately, our names were not called (yet, there is still a small possibility that we will be randomly selected for call-backs ;-). Anyway, we pretty much had no idea what to expect, so I'll let you know what goes on during the Wheelmobile Contestant Search.

We attended three of the "shows", one on Saturday and two on Sunday. Getting in line early helps you get a better seat in the audience, but by no means helps you in anyway to become a contestant. Everyone fills out a little color coded application (one color for each of the daily shows) that gets passed out about an hour before the show starts. Once they start letting you inside, everyone drops off their application into a bin. From there, you find a seat and can take the time to fill out more forms from their national sponsors. Sony Card was there giving away Pat and Vanna talking minis and blink-y pens for people who sign up for the Wheel Watchers club (nothing to those of us already a member) or a Sony Card and Buick was there to raffle off V.I.P. tickets to the Navy Pier taping (which you don't find out who wins during the show). When the show starts, the traveling Pat (Marty) and traveling Vanna (Heidi) draw random applications out of a bin and call out people's names. There are five people playing a round and five people waiting to play the next round on stage at any time. There ends up being about 30 names that are called for each show. Once on stage Marty spins a prize wheel for the contestant to go home with (Wheel of Fortune T-Shirts, Hats, Mini-Packs, etc.), then the contestants talk with Marty, he asks the basic questions that Pat would on the show: "What is your name?", "What do you do?", "What do you like to do in your free time?", etc. This is the part of the show that really matters for a wannabe contestant, they are judging your personality and energy, looking for someone who would be good on TV. After going through the introduction drill with the five contestants, they move on to a Speed-Up Round, where you have 3 seconds to call out a letter and another 3 seconds to try and come up with the solution to the puzzle. When somebody solves the puzzle, they all walk off the stage, collecting their goodies and returning to their seats.

One thing to note, that they go over a few times, is that being called up on stage or solving the puzzle do not guarantee you a call-back for the final try-outs. You have to wait for a letter or email from the Wheel inviting you back. They also reserve a few seats in the final try-outs for people randomly selected from the pool of people who did not get called on stage, so by showing up you still have a shot (although slim) of getting a call-back letter.

So, we didn't get called on stage, but it was still a fun experience. Now here is hoping we get randomly selected for the final try-outs or family week :-)

A Betta and Jamaica

This morning, my parents left for a week long vacation in Jamaica. Before they left, my mom bought Stephanie and I a pet, a male Betta fish that we named Azulie. Sorry, no pictures yet, I lent my Canon Digital Rebel XT EOS camera to my dad for their trip.

I've been pretty addicted to the Guitar Hero series lately and actually opted to leave my PS2 and the Guitar Hero series at my parents house while they are gone. I did this in order to have the time to finish the Legend of Zelda: Phantom Hourglass for the DS (which I just beat this morning!) and Super Paper Mario for the Wii (which I have left at chapter 8 for quite some time now). And, since Guitar Hero III comes out for the Wii in a couple weeks and Super Mario Galaxy soon thereafter, I need to get around to finishing the games I have started... Hard to do when I watch Curb Your EnthusiasmChuckHeroesHouseBack to You'Til DeathMy Name Is Earl, and The Office on a regular basis with other shows when I can...

Autotabbing with JavaScript

So, yesterday in Autotabbing Numeric Fields with JavaScript, I concluded with my thoughts on a generic version of autotab. Well, it turns out, creating the generic version of autotab was not that difficult. The big question I had in mind was how to know when a user types something of interest. The solution is to eliminate all of the special keys (as identified in Quirksmode article Javascript – Detecting keystrokes) and NOT check for alpha-numeric keys or symbols that can change depending on the users locale.

Our special key check function will accept a keyCode and return true if it matches a known special key and false otherwise:

function keyup_specialKey( code )
{
  if ( 0 == code )                  return true; // f1 - f12 (Opera Mac)
  if ( 5 == code || 6 == code )     return true; // help (Mac only. Firefox/Safari give different values.)
  if ( 8 == code )                  return true; // backspace
  if ( 9 == code )                  return true; // tab
  if ( 12 == code )                 return true; // num lock (Mac)
  if ( 13 == code )                 return true; // enter
  if ( 16 <= code && code <= 18 )   return true; // shift, ctrl (also cmd on Opera Mac), alt
  if ( 20 == code )                 return true; // caps lock
  if ( 27 == code )                 return true; // escape (also num lock on Opera Mac)
  if ( 33 <= code && code <= 40 )   return true; // page up, page down, end, home, arrow keys
  if ( 45 == code )                 return true; // insert (also help on Opera Mac)
  if ( 46 == code )                 return true; // delete
  if ( 91 == code )                 return true; // start
  if ( 112 <= code && code <= 123 ) return true; // f1 - f12
  if ( 144 == code )                return true; // num lock
  return false;
}

Since we only want to have one function that actually handles the autotab, we will refactor yesterday’s doNumericAutotab() function to make an autotab function. While we are at it, we will also factor out the numeric key check into it’s own function so that it can be reused as necessary. Our new autotab function will accept an element and a regular expression string and performs a match on the element’s value. If the match passes, we pass focus to the next form field (another function that we can factor out).

function doNumericAutotab( e )
{
  var elm = getElementFromEvent( e );
  var keyCode = e.keyCode;
  var numeric = "^\\d{" + elm.maxLength + "}$";
  
  // Return if we don't find the target element or non-numeric key is pressed.
  if ( !elm || !keyup_numericKey( keyCode ) ) return;
  
  autotab( elm, numeric );
}

function keyup_numericKey( code )
{
  if ( 48 <= code && code <= 57 )       return true; // number keys (top of keyboard)
  else if ( 96 <= code && code <= 105 ) return true; // number keys (on key pad)
  else return false;
}

function autotab( elm, valid )
{
  var test = RegExp(valid);
  
  if ( elm.value.match(test) != null )
  {
    focusOn( nextFormElement( elm ) );
  }
}

function nextFormElement( current )
{
  var f = current.form;
  
  for ( var i = 0; i < f.length; i++ )
  {
    if ( f[i] == current )
    {
      next = f[i+1]
      return next == null ? null : next;
    }
  }
}

function focusOn( elm )
{
  if ( elm == null ) return;
  
  try
  {
    elm.focus();
  }
  catch ( ex )
  {
    // Catch Mozilla exception when new focus field has autocomplete data.
  }
}

And to finish off, we need to write our Generic Autotab function that autotabs when the value of the form field length matches the form field’s maxLength value, regardless of what is actually written in field.

function doGenericAutotab( e )
{
  var elm = getElementFromEvent( e );
  var keyCode = e.keyCode;
  var all = "^.{" + elm.maxLength + "}$";
  
  // Return if we don't find the target element or a special key is pressed.
  if ( !elm || keyup_specialKey( keyCode ) ) return;
  
  autotab( elm, all );
}

If you want to write your own Autotab function, you can go right ahead. All you have to do is create a regular expression string that identifies when your form field is complete, and pass the element and regular expression to autotab. There are a lot of resources out there to help you with regular expressions, just do a quick search to get started.

autotab.js – to use: include the autotab.js file in the head of your document. You will need to update the init() function to suit your autotabbing needs, by default the function sets up Numeric Autotabbing on all form input fields that have the class autotab.