#!/usr/bin/python2.1
# vim:set ai et sts=2 sw=2:
# vim600: set fdm=marker cms=#%s :
#
# Photon is a static HTML gallery generator.
#   * directory structure is kept intact
#   * scaled images are put in separate directories (800x600, ...)
#   * etc...
#
#  Created: Luc Saillard <luc@sailard.org>  Wed, 13 Feb 2002 20:51:12 +0100
#  Last modified: Luc Saillard <luc.saillard@alcove.fr> Tue, 26 Mar 2002 22:51:40 +0100
#  Changelog:
#    v0.1: first release in Python.
#    v0.2.3: ???
#    v0.2.4: speed up improvement
#    v0.2.5: New <link> that indicates previous/next page
#            Key Shorcuts can now be use
#            Slideshow functions
#    v0.2.6: Javascript fix
#            Cmdline option fix
#            New exclude option
#
#
#  Copyright (C) 2002 Luc Saillard <luc@saillard.org>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Includes modules {{{
import getopt, os, sys, shutil, urllib, re 
from fnmatch import *
from stat import *
from array import array
from PIL import Image, ImageFile
import EXIF
# ------------------------------------------------------------------------ }}}
""" Global options """ # {{{
options = {}
options['verbose_level'] = 0
options['output_directory'] = './photos'
options['comment_filename'] = '.comments'
options['thumbsize'] = (160,120)
options['sizelist'] = [(0,0), (1024,768), (800,600), (640,480)]
options['forcecreate_html'] = 0
options['display_columns'] = 4
options['display_lines'] = 5
options['generate_root_index_html'] = 1
#options['img_bgcolor'] = "#eee5de"
#options['img_bgcolor'] = "#fff5ee"
options['img_bgcolor'] = "#fffaf5"
options['body_bgcolor'] = "#ccccff"
options['exif'] = 0
options['exif_bordercolor'] = "#008000"
options['exif_bgcolor'] = "#f0fff0"
options['exif_fgcolor'] = "black"
options['javascript'] = 1
options['exclude'] = ["*.mov","*.avi"]

photon_version = '0.2.6'

# ------------------------------------------------------------------------ }}}
#
# Main functions
#
def main():# {{{
  """ main procedure """

  short_opts="c:d:EfhIl:o:s:t:Vv"
  long_opts=[ "comment=", "display-columns=", "no-exif", "force", "help",
              "no-index", "display-lines=", "output-directory=", "sizelist=",
              "thumbsize", "version",  "verbose", "javascript",
              "exif-bordercolor=", "exif-bgcolor=", "exif-fgcolor=",
              "body-bgcolor=", "img-bgcolor=", 'exclude='
            ]
  try:
    opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
  except getopt.GetoptError, msg:
    # print help information and exit:
    if msg:
      print msg
    usage()
    sys.exit(2)
  for o, a in opts:
    if o in ("-c", "--comment"):
      options['comment_filename'] = a
    elif o in ("-d", "--display-columns"):
      if a.isdigit():
        options['display_columns'] = int(a)
      else:
        print "Bad argument:",a,"must be a number"
    elif o in ("-E", "--no-exif"):
      options['exif'] = 0
    elif o in ("-f", "--force"):
      options['forcecreate_html'] = 1
    elif o in ("-h", "--help"):
      usage()
      sys.exit()
    elif o in ("-I", "--no-index"):
      options['generate_root_index_html'] = 0
    elif o in ("-J", "--no-javascript"):
      options['javascript'] = 0
    elif o in ("-l", "--display-lines"):
      if a.isdigit():
        options['display_lines'] = int(a)
      else:
        print "Bad argument:", a, "must be a number"
    elif o in ("-o", "--output-directory"):
      options['output_directory'] = a
    elif o in ("-s", "--sizelist"):
      l=[]
      for s in a.split(','):
        if s == '0':
          l.append((0,0))
        else:
          m = re.search("^(\d+)x(\d+)$",s)
          if m:
            l.append((int(m.group(1)), int(m.group(2))))
          else:
            print "Bad size:", s, "must in the form 320x240"
      # End: for s in a.split(','):
      options['sizelist'] = l
    elif o in ("-t", "--thumbsize"):
      m = re.search("^(\d+)x(\d+)$",s)
      if m:
        options['thumbsize'] = ((int(m.group(1)),int(m.group(2))))
      else:
        print "Bad size:", s, "must in the form 320x240"
    elif o in ("-V", "--version"):
      print "Photon", photon_version
      sys.exit()
    elif o in ("-v", "--verbose"):
      options['verbose_level'] += 1
    elif o == "--exif-bordercolor":
      if match_color_type(a):
        options['exif_bordercolor'] = a
      else:
        print "Bad color:", a, "must in the form #0055ff or white"
    elif o == "--exif-bgcolor":
      if match_color_type(a):
        options['exif_bgcolor'] = a
      else:
        print "Bad color:", a, "must in the form #0055ff or white"
    elif o == "--exif-fgcolor":
      if match_color_type(a):
        options['exif_fgcolor'] = a
      else:
        print "Bad color:", a, "must in the form #0055ff or white"
    elif o == "--body-bgcolor":
      if match_color_type(a):
        options['body_bgcolor'] = a
      else:
        print "Bad color:", a, "must in the form #0055ff or white"
    elif o == "--img-bgcolor":
      if match_color_type(a):
        options['img_bgcolor'] = a
      else:
        print "Bad color:", a, "must in the form #0055ff or white"
    elif o == "--exclude":
      options['exclude'].append(a)

  print_options()

  if options['thumbsize'] not in options['sizelist']:
    options['sizelist'].append(options['thumbsize'])
  # Fix a bug when decompressing -> compressing a jpeg file
  ImageFile.MAXBLOCK = 1000000 # default is 64k

  if args:
    for path in args:
      process_directory(path,"")
  else:
    usage()
    sys.exit()

# ------------------------------------------------------------------------ }}}
def usage(): # {{{
  """ Print information to use this program """
  print """
Usage: photon.py [OPTION]... [PATH]...

Options:
  -c NAME --comment               Name of the comment file (default .comments)
  -d NUM  --display-columns=NUM   Number of columns in index (default 3)
  -E      --no-exif               Don't include EXIF information in HTML file
  -f      --force                 Overwrite image files (default no)
  -h      --help                  Print this help
  -I      --no-index              Do not generate the high level index.html
  -J      --no-javascript         Do not use javascript (no shortcuts, ... )
  -l NUM  --display-lines=NUM     Number of lines in index (default 5)
  -o NAME --output-directory=NAME Name of the output directory
  -s LIST --sizelist=LIST         Image sizes (default 0,1024x768,800x600,640x480)
  -t LIST --sizelist=LIST         Size of thumbnails (default 160x120)
  -V      --version               Print Photon version
  -v      --verbose               Be verbose
          --exif-bordercolor=COLOR  Exif window border color (default #008000)
          --exif-bgcolor=COLOR    Exif window background color (default #f0fff0)
          --exif-fgcolor=COLOR    Exif window text color (default 'black')
          --body-bgcolor=COLOR    Body background color (default #ccccff)
          --img-bgcolor=COLOR     Image background color (default 'white')
          --exclude=PATTERN       Exclude files matching PATTERN

Shortcuts while viewing an image:
  n/SPACE     Go to the next image (with the same resolution)
  p/BACKSPACE Go to the previous image (with the same resolution)
  s           Start/Stop the slideshow
  +/-         Increase/Decrease by one second the slideshow's period
  z           Change to the higher resolution [NOT YET IMPLEMENTED]

""" 
# ------------------------------------------------------------------------ }}}
def print_options():# {{{
  """ Print Options array that contains all configurable options """

  if options['verbose_level'] >= 1:
    print "Program options:"
    for key in options.keys():
      print " ", key, options[key]
# ------------------------------------------------------------------------ }}}
#
# Functions that work on a directory or on a file
#
def process_directory(realpath, relativepath): # {{{
  """ Generate thumbmails, html pages for this directory """

  directories_list = []
  images_list = []

  destdir = os.path.join(options['output_directory'], relativepath)
  safe_mkdir(destdir)

  sourcedir = os.path.join(realpath, relativepath)

  # For each images build alls sub-image,
  # for each directory, recurse into them
  for entry in os.listdir(sourcedir):
    # Don't accept directory or file beginning with a dot
    if entry[0] == '.': continue
    pathname = os.path.join(sourcedir, entry)
    for pattern in options['exclude']:
      if fnmatch(pathname,pattern):
        print 'Excluding %s' % pathname
        break
    else:
      mode = os.stat(pathname)[ST_MODE]
      if S_ISDIR(mode):
        process_directory(realpath, os.path.join(relativepath, entry))
        directories_list.append(entry)
      elif S_ISREG(mode):
        picinfo = process_file(realpath, relativepath, entry)
        if picinfo:
          images_list.append(picinfo)
      else:
        print 'Skipping %s' % pathname

  process_comment_file(realpath, relativepath, images_list)

  # Now, we have the complete list of directory, and files ... sort them
  images_list.sort(lambda x, y: cmp(x['filename'], y['filename']))
  directories_list.sort()
  # ... then generate html pages
  make_directory_html(relativepath,directories_list, images_list)
  if len(images_list) > 0:
    make_image_html(relativepath,directories_list, images_list)

# ------------------------------------------------------------------------ }}}
def process_file(sourcedir, relativepath, filename): # {{{
  """ Create for this file all thumbmails and return the size of the image """

  srcfile = os.path.join(sourcedir, relativepath, filename)

  # Keep only Jpeg file (python cores dump with some PCD (kodak) file
  if ((filename.lower().rfind(".jpg") == -1) and
      (filename.lower().rfind(".jpeg") == -1)) :
    return None

  pic = {}
  try:
    im = Image.open(srcfile)
    print 'Processing image', srcfile, im.format, "%dx%d" % im.size, im.mode
  except IOError, err:
    print "cannot create thumbnail for", srcfile ,"(", err.strerror, ")"
  else:
    # Foreach size of the image, resize them only when it's different from Original
    pic['filename'] = filename
    pic['original_size'] = im.size
    if options['exif']:
      file = open(srcfile, 'rb')
      pic['exif'] = EXIF.process_file(file)
    if im.size[1] > im.size[0]: # Keep aspect ration
      pic['ratio'] = 34
    else:
      pic['ratio'] = 43
    for (w,h) in options['sizelist']:
      if w == 0 and h == 0: # Special case when it is a original file
        subdir = 'original'
        destdir = os.path.join(options['output_directory'], relativepath, subdir)
        destfile = os.path.join(destdir, filename)
	if not compare_lastmodifiedtime(srcfile, destfile):
          safe_mkdir(destdir)
          shutil.copyfile(srcfile, destfile)
      else:
	if pic['ratio']==34:
          newsize = (h,w)
	else:
          newsize = (w,h)
        subdir='%dx%d' % (w,h)
        destdir = os.path.join(options['output_directory'], relativepath, subdir)
        destfile = os.path.join(destdir, filename)
	if not compare_lastmodifiedtime(srcfile, destfile):
          if w > im.size[0]: # Don't generate thumbmail when original file is smaller
            print "Skipping", srcfile, "for resolution %dx%d" % newsize
            continue
          safe_mkdir(destdir)
	  try:
            im.resize(newsize, Image.BICUBIC).save(destfile, 'JPEG', optimize=1, progressive=1)
          except IOError, err:
            print "Error while writing thumbnail, will try without optimization..."
            print "Perhaps you can try to increase ImageFile.MAXBLOCK in the source file"
            try: # Try to save the Jpeg file without progessive option
              im.resize(newsize,Image.BICUBIC).save(destfile, 'JPEG')
            except IOError, err:
              print "cannot create ", destfile, "(", err, ")"
    return pic

# ------------------------------------------------------------------------ }}}
def process_comment_file(realpath, relativepath, images_list): # {{{
  """ Process the .comments in this directory that contains a comment for an image """

  try:
    f = open(os.path.join(realpath, relativepath, options['comment_filename']), 'r')

    r = re.compile('^"([^"]+)"\s+"([^"]+)"')
    s = f.readline()
    while s:
      m = r.search(s)
      if m:
        # Ok we found a filename, and a comment in this line
        # TODO: use map function ?
        for i in images_list:
          if i['filename'] == m.group(1):
            i['comments'] = m.group(2)
            break
      # silently discard bad line ?
      s = f.readline()
    f.close()
  except IOError:
    pass 

# ------------------------------------------------------------------------ }}}
def make_directory_html(relativepath, directories_list, images_list): # {{{
  """ Make all html page for index this directory """

  if relativepath == "" and not options['generate_root_index_html']:
    return

  images_processed = 0 # Number of the images currently processed in the list
  page_index = 0       # Number of index.html page currently processed
  total_images = len(images_list) + len(directories_list)
  items_per_page = options['display_columns'] * options['display_lines'];
  output_directory = os.path.join(options['output_directory'], relativepath)

  while images_processed < total_images:

    if images_processed == 0:
      index_html_filename = 'index.html'
    else:
      index_html_filename = 'index%d.html' % (images_processed / items_per_page)

    f = open(os.path.join(output_directory, index_html_filename), 'w')
    output_index_html_header(f, relativepath, None, None)
    if images_processed == 0:
      output_index_html_directories(f, directories_list)
      total_images -= len(directories_list) # Small hack, because i want to do a do {} while(x)
    output_index_html_images(f, images_list[images_processed : images_processed + items_per_page])
    output_index_html_footer(f, page_index, images_processed, total_images)

    f.close()

    images_processed += items_per_page
    page_index += 1

# ------------------------------------------------------------------------ }}}
def make_image_html(relativepath, directories_list, images_list): # {{{
  """ Make all html page for all images """

  make_blank_gif(os.path.join(options['output_directory'], relativepath, 'blank.gif'))
  if options['exif']:
    make_exif_js(os.path.join(options['output_directory'], relativepath, 'exif.js'))
  if options['javascript']:
    make_shortcut_js(os.path.join(options['output_directory'], relativepath, 'shortcuts.js'))

  output_directory = os.path.join(options['output_directory'], relativepath)

  total_images = len(images_list)
  k = 0

  while k < total_images:

    image = images_list[k]

    for resolution in options['sizelist']:

      image_html_filename = image_filename_to_image_htmlfilename(image['filename'], resolution)

      # Calculate the previous and next image/link
      if k == 0:
        previous_image = None
        previous_link  = None
      else:
        previous_image = images_list[k - 1]
        previous_link  = image_filename_to_image_htmlfilename(previous_image['filename'], resolution)

      if k + 1 >= len(images_list):
        next_image = None
        next_link  = None
      else:
        next_image = images_list[k + 1]
        next_link  = image_filename_to_image_htmlfilename(next_image['filename'], resolution)

      # Make the html page for this image and this resolution
      f = open(os.path.join(output_directory, urllib.unquote(image_html_filename)), 'w')
      output_image_html_header(f, relativepath, image['filename'], previous_link, next_link)
      output_image_html_body(f, relativepath, images_list, k, resolution)
      if options['exif']:
        output_image_html_exif_window(f, image)
      output_image_html_footer(f)
      f.close()
    # End: for resolution in options['sizelist']:

    k += 1

  # End: for image in images_list

# ------------------------------------------------------------------------ }}}
#
# Functions that output HTML Code
#
def output_index_html_header(f, relativepath, previous_link, next_link): # {{{
  """ Write in the file, header of an index.html page (body included) """

  if relativepath == "":
    html_title = "Albums";
  else:
    html_title = relativepath.split(os.sep)[-1];

  navbar = navbar_for_index_html(relativepath)


  f.write("""
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
 <title>Photon: %s</title>
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
""" % html_title)

  if previous_link:
    f.write(" <link rel=\"prev\" href=\"%s\">" % previous_link)
  if next_link:
    f.write(" <link rel=\"next\" href=\"%s\">" % next_link)

  f.write("""
</head>
<body bgcolor="%s">
<div align=center>
<table bgcolor="black">
<tr>
 <td>
  <table width="100%%" cellpadding=4>
  <tr bgcolor="%s">
   <td colspan=%s>%s</td>
  </tr>
""" % (options['body_bgcolor'], options['img_bgcolor'],
      options['display_columns'], navbar))

# ------------------------------------------------------------------------ }}}
def output_index_html_directories(f, directories_list): # {{{
  """ Output a HTML table that contains the directories list """

  column = 0

  f.write('  <tr bgcolor="%s">\n' % options['img_bgcolor'])

  # Create a case for each directory
  for d in directories_list:
    if column >= options['display_columns']:
      f.write('  </tr>\n  <tr bgcolor="%s">\n' % options['img_bgcolor']);
      column = 0
    f.write('   <td align=center><a href="%s/index.html"><b>%s</b></a><br><img width=%d height=1 src="blank.gif" alt=""></td>' % (urllib.quote(d),d,options['thumbsize'][0]))
    column += 1
  
  # Fill the empty case ...
  if column > 0:
    while column < options['display_columns']:
      f.write('   <td><img width=%d height=1 src="blank.gif" alt=""></td>' % options['thumbsize'][0])
      column += 1
    # Close the row
    f.write('  </tr>\n')

# ------------------------------------------------------------------------ }}}
def output_index_html_images(f, images_list):  # {{{
  """ Output a HTML table that contains the images list """
  
  column = 0

  f.write('  <tr bgcolor="%s">\n' % options['img_bgcolor'])

  # Create a case for each image
  for pic in images_list:
    if column >= options['display_columns']:
      f.write('  </tr>\n  <tr bgcolor="%s">\n' % options['img_bgcolor']);
      column = 0

    if pic['ratio'] == 34:
      thumbsize_width  = options['thumbsize'][1]
      thumbsize_height = options['thumbsize'][0]
    else:
      (thumbsize_width, thumbsize_height) = options['thumbsize']
 
    imgurl = urllib.quote(os.path.join("%dx%d" % options['thumbsize'], pic['filename']))
    imghtmlurl = image_filename_to_image_htmlfilename(pic['filename'], options['sizelist'][0])

    f.write('   <td valign=bottom align=center><table border=0><tr><td bgcolor=black><a href="%s"><img hspace=3 vspace=3 border=0 src="%s" alt="%s" width=%d height=%d></a></td></tr></table><br><a href="%s"> %s </a></td>\n' % (imghtmlurl,imgurl, pic['filename'], thumbsize_width, thumbsize_height,imghtmlurl, pic['filename']))
    column += 1

  # Fill the empty case ...
  if column > 0:
    while column < options['display_columns']:
      f.write('   <td><img width=%d height=1 src="blank.gif" alt=""></td>' % options['thumbsize'][0])
      column += 1
    # Close the row
    f.write('  </tr>\n')

# ------------------------------------------------------------------------ }}}
def output_index_html_footer(f, page_index, images_processed, total_images): # {{{
  """ Output the footer for a index.html page """

  items_per_page = options['display_columns'] * options['display_lines'];
  f.write('  <tr bgcolor="%s"><td colspan=%s><table border=0 width="100%%"><tr><td>' 
    % (options['img_bgcolor'],options['display_columns']))
  # Output the Start and the Prev link
  if page_index == 0:
    f.write('Start | Prev %s | ' % items_per_page)
  elif page_index == 1:
    f.write('<a href="index.html">Start</a> | <a href="index.html">Prev %s</a> | ' % items_per_page)
  else:
    f.write('<a href="index.html">Start</a> | <a href="index%d.html">Prev %s</a> | ' % (page_index - 1, items_per_page))

  # Output the Next link
  max_pages = total_images / items_per_page
  if (total_images % items_per_page) == 0:
    max_pages -= 1
  if page_index < max_pages:
    f.write('<a href="index%d.html">Next %s</a> | ' % (page_index + 1, items_per_page))
  else:
    f.write('Next %s | ' % items_per_page)

  # Output the End link
  if page_index < max_pages:
    f.write('<a href="index%d.html">End</a></td>' % max_pages)
  else:
    f.write('End</td>')

  # Output the Number of Images
  if total_images == 0:
    f.write('<td align=right>&nbsp;</td>')
  else:
    max_images_displayed = images_processed + items_per_page
    if max_images_displayed > total_images:
      max_images_displayed = total_images
    f.write('<td align=right>Images %d to %d of %d</td>' 
       % (images_processed + 1, max_images_displayed, total_images))

  f.write("""</td>
    </tr>
    </table>
   </td>
  </tr>
  </table>
 </td>
</tr>
</table>
<font size=-1>Generated by photon %s</font>
</div>
</body>
</html>
""" % photon_version)
          
# ------------------------------------------------------------------------ }}}
def output_image_html_header(f, relativepath, imagefilename, previous_link, next_link): # {{{
  """ Write in the file, header of an image.html page (body included) """

  f.write("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
 <title>Photon: %s</title>
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> 
 <style type="text/css">
   #exifwindow {position:absolute; height:1; width:1; top:0; left:0;}
 </style>
""" % imagefilename)

  if options['exif']:
    f.write (" <script language=\"JavaScript1.2\" src=\"exif.js\"></script>\n")
  if options['javascript']:
    f.write (" <script language=\"JavaScript1.2\" src=\"shortcuts.js\"></script>\n")
  if previous_link:
    f.write(" <link id=\"previous_image\" rel=\"prev\" href=\"%s\">\n" % previous_link)
  if next_link:
    f.write(" <link id=\"next_image\" rel=\"next\" href=\"%s\">\n" % next_link)

  f.write("</head>")

  if options['exif']:
    f.write("<body bgcolor=\"%s\" onload=\"exif_init()\">\n" % options['body_bgcolor'])
  else:
    f.write("<body bgcolor=\"%s\">\n" % options['body_bgcolor'])


# ------------------------------------------------------------------------ }}}
def output_image_html_body(f, relativepath, images_list, index, resolution): # {{{
  """ Output in the file f, the content of the body for this image """

  imageinfo = images_list[index]
  if index == 0:
    previous_image = None
  else:
    previous_image = images_list[index - 1]

  if index + 1 >= len(images_list):
    next_image = None
  else:
    next_image = images_list[index + 1]

  resolution_string = "%dx%d" % resolution

  if resolution_string == "0x0":
    resolution_string = "original"
  image_urlname = urllib.quote(os.path.join(resolution_string, imageinfo['filename']));

  vars = { 'img_bgcolor'     : options['img_bgcolor'],
           'display_columns' : options['display_columns'],
           'navbar'          : navbar_for_image_html(relativepath, images_list, index),
           'imgsrc'          : urllib.quote(os.path.join(resolution_string, imageinfo['filename'])),
           'imgalt'          : imageinfo['filename'],
           'version'         : photon_version,
           'body2'           : image_html_body_sub(imageinfo, resolution, previous_image, next_image)}
  
  body = """<div align=center>
<table bgcolor="black">
<tr>
 <td>
  <table width="100%%" cellpadding=4>
  <tr bgcolor="%(img_bgcolor)s">
   <td colspan=%(display_columns)s>%(navbar)s</td>
  </tr>
  <tr bgcolor="%(img_bgcolor)s">
   <td colspan=%(display_columns)s><center><table border=0><tr><td bgcolor=black><img hspace=3 vspace=3 border=0 src="%(imgsrc)s" alt="%(imgalt)s"></td></tr></table></center></td>
  </tr>
""" % vars

  if imageinfo.has_key('comments'):
    body += """
  <tr bgcolor="%s">
   <td colspan=%s><center>%s</center></td>
  </tr>""" % (options['img_bgcolor'],options['display_columns'],imageinfo['comments'])

  body += """
  <tr bgcolor="%(img_bgcolor)s">
   <td colspan=%(display_columns)s>%(body2)s</td>
  </tr>
  </table>
 </td>
</tr>
</table>
<font size=-1>Generated by Photon %(version)s</font>
</div>
""" % vars

  f.write(body)

# ------------------------------------------------------------------------ }}}
def image_html_body_sub(imageinfo, resolution, previous_image, next_image): # {{{
  """ Return the HTML code to display 2 thumbmails to include in the image page """

  left_img = '<table border=0><tr><td valign=top width=%d>' % options['thumbsize'][0]
  if previous_image:
    thumblink = image_filename_to_image_htmlfilename(previous_image['filename'], resolution)
    thumbimg = os.path.join("%dx%d" % options['thumbsize'], urllib.quote(previous_image['filename']))
    left_img += '<a href="%s">&lt;-Prev</a><br><table border=0><tr><td bgcolor=black><a href="%s"><img align=middle hspace=2 vspace=2 border=0 src="%s" alt="%s"></a></td></tr></table>' % (thumblink, thumblink, thumbimg, previous_image['filename'])
  else:
    left_img += '<img width=%d height=1 src="blank.gif" alt="">' % options['thumbsize'][0]
  left_img += '</td></tr></table>'

  right_img = '<table border=0><tr><td valign=top align=right width=%d>' % options['thumbsize'][0]
  if next_image:
    thumblink = image_filename_to_image_htmlfilename(next_image['filename'], resolution)
    thumbimg = os.path.join("%dx%d" % options['thumbsize'], urllib.quote(next_image['filename']))
    right_img += '<a href="%s">Next -&gt;</a><br><table border=0><tr><td bgcolor=black><a href="%s"><img align=middle hspace=2 vspace=2 border=0 src="%s" alt="%s"></a></td></tr></table>' % (thumblink, thumblink, thumbimg, next_image['filename'])
  else:
    right_img += '<img width=%d height=1 src="blank.gif" alt="">' % options['thumbsize'][0]
  right_img += '</td></tr></table>'

  if options['exif']:
    sizelist = '<form action=""><input type="button" onclick="exif_hide_show()" value="Image Information"></form><br>'
  else:
    sizelist = ''
  if len(options['sizelist']):
    sizelist += "Other sizes:<br>"
    for r in options['sizelist']:
      if r != resolution:
        if r[0] > imageinfo['original_size'][0]: # Don't add this resolution when original file is smaller
          continue
        if r == (0,0):
          r_str = "Original"
        else:
	  if imageinfo['ratio'] == 34:
            r_str = "%dx%d" % (r[1], r[0])
          else:
            r_str = "%dx%d" % r
        sizelist += '<a href="%s">%s</a><br>' % (image_filename_to_image_htmlfilename(imageinfo['filename'], r), r_str)
      # End: if r != resolution:
    # End: for r in options['sizelist']:
  # End: if len(options['sizelist']):

  return """
    <table border=0 width="100%%">
    <tr>
     <td valign=top>%s</td>
     <td valign=top align=center>%s</td>
     <td valign=top align=right>%s</td>
    </tr>
    </table>
   """ % (left_img,sizelist,right_img)


# ------------------------------------------------------------------------ }}}
def output_image_html_exif_window(f, imageinfo): # {{{
  """ Output the HTML code for the exif window page """

  exif_info = ""

  if imageinfo.has_key('exif') and imageinfo['exif'].has_key('Image Make'):
    exif_info += "Taken with a %s %s<br>" % (imageinfo['exif']['Image Make'].printable, imageinfo['exif']['Image Model'].printable)

  for prop in imageinfo['exif'].keys():
    if prop in ('EXIF ExifImageLength', 'EXIF ExifImageWidth', 'EXIF DateTimeDigitized', 'EXIF ExposureTime'):
      exif_info += '<b>%s</b>: %s<br>' % (prop, imageinfo['exif'][prop].printable)

  
  f.write("""
<div id="exifwindow" style="visibility:hidden">
  <table width="1%%" height="1%%" bgcolor="%s"><tr><td><table width="300" height="250" bgcolor="%s">
   <tr><td align="left" valign="middle">%s</td></tr>
   <tr><td align="right" valign="bottom"><a href="" onclick="exif_hide(); return false;">Close this box</a></td></tr>
  </table></td></tr></table>
</div>
""" % (options['exif_bordercolor'], options['exif_bgcolor'], exif_info))
    
# ------------------------------------------------------------------------ }}}
def output_image_html_footer(f): # {{{
  """ Output the footer for a image.html page """
  f.write("</body>\n</html>\n")
# ------------------------------------------------------------------------ }}}
#
# Useful misc function
#
def safe_mkdir(pathname): # {{{
  """ Create a directory only when it doesn't exist """

  try :
    mode = os.stat(pathname)[ST_MODE]
    if not S_ISDIR(mode):
      os.mkdir(pathname);
  except OSError:
    os.mkdir(pathname);

# ------------------------------------------------------------------------ }}}
def navbar_for_index_html(relativepath): # {{{
  """ Transform the directory location in a navigation bar printable in HTML """

  if relativepath == "":
    return "<b>Home</b>"

  ndirs = 1 + relativepath.count(os.sep)
  site_home = "../" * ndirs
  url = '<a href="%sindex.html">Albums</a>' % site_home

  for d in relativepath.split(os.sep):
    if d == "":
      continue
    ndirs -= 1
    if ndirs:
      url += ' -&gt; <a href="' + "../" * ndirs + 'index.html">' + d + '</a>'
    else:
      url += ' -&gt; <b>' + d + '</b>'
  return url

# ------------------------------------------------------------------------ }}}
def navbar_for_image_html(relativepath, images_list, index): # {{{
  """ Print the navigation bar for an image.html
  relativepath: Path where to found the image
  images_list: list of the images for this directory
  index: index to the current image in the images_list
  """

  # Calculate the page number where this page is located
  items_per_page = options['display_columns'] * options['display_lines'];
  current_page = index / items_per_page

  if relativepath == "":
    if current_page:
      return '<a href="index%d.html">Albums</a>' % current_page
    else:
      return '<a href="index.html">Albums</a>'

  ndirs = 1 + relativepath.count(os.sep)
  site_home = "../" * ndirs
  url = '<a href="'+site_home+'index.html">Albums</a>'

  for d in relativepath.split(os.sep):
    if d == "":
      continue
    ndirs -= 1
    if ndirs:
      url += ' -&gt; <a href="' + "../" * ndirs + 'index.html">' + d + '</a>'
    else:
      if current_page:
        url += ' -&gt; <a href="index%d.html">%s</a>' % (current_page, d)
        url += ' -&gt; <b>' + images_list[index]['filename'] + '</b>'
      else:
        url += ' -&gt; <a href="index.html">' + d + '</a> -&gt; <b>' + images_list[index]['filename'] + '</b>'
  return url

# ------------------------------------------------------------------------ }}}
def image_filename_to_image_htmlfilename(imagename, resolution): # {{{
  """ Return the name of the HTML page for an image and a resolution """
  # Strip .jpg from filename
  i = imagename.find(".")
  image_filename_without_ext = imagename[0:i]

  # if this image is the original size, do not append the resoltuion to the filename
  resolution_string = "%dx%d" % resolution
  if resolution_string == "0x0":
    image_html_filename="%s.html" % image_filename_without_ext;
  else:
    image_html_filename="%s_%s.html" % (image_filename_without_ext, resolution_string);
  return urllib.quote(image_html_filename)

# ------------------------------------------------------------------------ }}}
def compare_lastmodifiedtime(pathname1, pathname2): # {{{
  """ Compare the last modified time from 2 files (or directory) """

  if options['forcecreate_html']:
    return None
  try :
    mode1 = os.stat(pathname1)[ST_MTIME]
    mode2 = os.stat(pathname2)[ST_MTIME]
    return (mode1 < mode2)
  except OSError:
    return None

# ------------------------------------------------------------------------ }}}
def make_blank_gif(pathname): # {{{
  """ Make a small gif 1x1 with a transparent color """
  blank_gif="\107\111\106\070\071\141\001\000\001\000\200\000\000\000\000\000"\
            "\377\377\377\041\371\004\001\000\000\000\000\054\000\000\000\000"\
            "\001\000\001\000\100\002\001\104\000\073"

  try :
    f = open(pathname,'wb')
  except OSError:
    return 0
  else:
    f.write(blank_gif)
    f.close()
    
# ------------------------------------------------------------------------ }}}
def make_shortcut_js(pathname): # {{{
  """ Make a separate javascript file that contains shortcut functions """

  try :
    f = open(pathname, 'w')
  except OSError:
    return 0
  else:
    f.write("""
// Some variables to autodetect browser type
var ns=(document.layers);
var ie=(document.all);
var w3=(document.getElementById && !ie);

var timeout=5000;

// Change the current page to the id found in the page
function jumpto(id)
{
   if(!ns && !ie && !w3) 
     return;
   if (ie)
     e=eval("document.all." + id);
   else if (ns)
     e=eval('document.links[id]');
   else if (w3)
     e=eval('document.getElementById(id)');
  if ((e != null) && (e.href != null)) {
    if (mytimeout) {
      location.href = e.href + "?slideshow=" + timeout;
    } else {
      location.href = e.href;
    }
  }
}

// Change the current page
function next_page()
{
  jumpto('next_image');
}

function previous_page()
{
  jumpto('previous_image');
}

// Activate/Deactivate the slideshow
var mytimeout = 0;
function toggle_slideshow()
{
  if (!mytimeout)
   {
     mytimeout = setTimeout("next_page()",timeout);
     window.status='Slideshow set to ' + (timeout/1000) + ' seconds';
   }
  else
   {
     clearTimeout(mytimeout);
     mytimeout=0;
     window.status='Stopping Slideshow';
   }
}

// Manage timeout for the slideshow
function modify_timeout(t)
{
  timeout+=t;
  if (timeout<1000)
    timeout=1000;
  if (mytimeout)
  { // If the counter is active, reactivate it !
    toggle_slideshow();
    toggle_slideshow();
  }
  else
  {
     window.status='Slideshow timeout set to ' + (timeout/1000) + ' seconds';
  }
}

// Event Handler that receive Key Event
function getkey(e)
{
  if (e == null)
   { // IE
     kcode = window.event.keyCode;
   } 
  else
   { // Mozilla
     kcode = e.which;
   }
  key = String.fromCharCode(kcode).toLowerCase();
//  alert("pressed " + kcode + " <=> " + key);
  switch(key)
   {
     case "n":
     case " ":
       next_page();
       return false;
     case "p":
       previous_page();
       return false;
     case "s":
       toggle_slideshow();
       return false;
     case "+":
       modify_timeout(1000);
       return false;
     case "-":
       modify_timeout(-1000);
       return false;
   }
  switch(kcode)
   {
     case 8:
       previous_page();
       return false;
   }
  return true;
}

if(w3 || ie)
{
  document.onkeypress = getkey;
} 
else
{
  document.captureEvents(Event.KEYUP);
  document.onkeyup = getkey; 
  document.captureEvents(Event.KEYPRESS);
  document.onkeypress = getkey;
}

// Test if the slideshow is active
var argstr = location.search.substring(1, location.search.length)
var args = argstr.split('&');
for (var i = 0; i < args.length; i++)
{
  var arg = unescape(args[i]).split('=');
  if (arg[0] == "slideshow") 
   { // ... and set timeout according to the last value
     timeout=parseInt(arg[1]);
     toggle_slideshow();
   }
}


""")
    f.close()
def make_exif_js(pathname): # {{{
  """ Make a separate javascript file that contains dynamic html functions """

  try :
    f = open(pathname, 'w')
  except OSError:
    return 0
  else:
    f.write("""
var ns=(document.layers);
var ie=(document.all);
var w3=(document.getElementById && !ie);
var exif_layer;
var exif_isshow;

function exif_init()
{
   if(!ns && !ie && !w3) 
     return;
   if (ie)
     exif_layer=eval('document.all.exifwindow.style');
   else if (ns)
     exif_layer=eval('document.layers["exifwindow"]');
   else if (w3)
     exif_layer=eval('document.getElementById("exifwindow").style');
   
   exif_hide();
}

function exif_show()
{
  if (ie)
   {
     documentWidth  =document.body.offsetWidth/2+document.body.scrollLeft-20;
     documentHeight =document.body.offsetHeight/2+document.body.scrollTop-20;
     exif_layer.visibility="visible";
   }    
  else if (ns)
   {
     documentWidth=window.innerWidth/2+window.pageXOffset-20;
     documentHeight=window.innerHeight/2+window.pageYOffset-20;
     exif_layer.visibility ="show";
   }
  else if (w3)
   {
     documentWidth=self.innerWidth/2+window.pageXOffset-20;
     documentHeight=self.innerHeight/2+window.pageYOffset-20;
     exif_layer.visibility="visible";
   }
  exif_layer.left=documentWidth-150;
  exif_layer.top =documentHeight-125;
  exif_isshow=1;
  setTimeout("exif_hide()",10000);
}

function exif_hide()
{
  if (ie||w3)
    exif_layer.visibility="hidden";
  else
    exif_layer.visibility="hide";
  exif_isshow=0;
}

function exif_hide_show()
{
  if (exif_isshow)
    exif_hide()
  else
    exif_show()
}
""")
    f.close()
    
# ------------------------------------------------------------------------ }}}
def match_color_type(color): # {{{
  """ Return the string when the string match a color in HTML format """
  if color[0] == "#":
    if re.match("^#[0-9a-f]{6}$",color):
      return color
  elif re.match("^[a-z]+$",a):
    return color
  return None

# ------------------------------------------------------------------------ }}}

import profile

if __name__ == "__main__":
    profile.run('main()')
    main()


