# # This python program will create a thumbnail page given a directory # which contains a set of JPG images. # # To run from the command line, pass in 1-4 parameters as follows: # (1) The directory which contains the thumbnails. # (2) The output file name. If missing it will be the name of the # directory appended with "_thumb.gon" # (3) The title for the thumbnail page. If missing it will be the # directory name (w/o path) appended with "Pictures" # (4) The relative path to the html file which contains the text. If # this is blank then no backlink will be created. If missing, it # will be the name of the directory appended with '.html' (unless # the file with the .go extension does not exist; in which case # the relative path will be blank.) # # To run from another Python script, execute the subroutine "run" with the # same parameters. # # All the images must be named in the Gould standard image naming convention. # # 990604_1949_BethPlaying.jpg or # 990604_BethPlaying.jpg or # _BethPlaying.jpg # # The file name consists of three fields, separated with underscores. The # first field is the date the picture was taken (yymmdd). The second field is # the time the picture was taken (hhmm). The last field is the title of the # picture which each separate word capitalized. # # From the orginals, we will create two other derived images. A thumbnail # (100x100) which will have the same name except for a prefix of "s" and # a 700x440 image optimized for easy viewing which will have the same name # except for a prefix of "l". # # 990604_1949_BethPlaying.jpg full size # s990604_1949_BethPlaying.jpg 100x100 thumbnail # l990604_1949_BethPlaying.jpg 700x440 scaled version # # Note 1: sometimes the images are already less than 700x440. In that case we # do not create a scaled version and only support a fullsize version in the # thumbnail page. # # When this Python programn is run, all the thumbnail and scaled versions of # each image will be (re)created from the original, full size images. Then # a single HTML-like output file will be created. # # The output file will contain thumbnails and links for all the images in the # directory. There will be a maximum of 5 images per line and all the # thumbnail images will be organized in a table. # # To keep the page from getting too large, we put a maximum of 40 thumbnails # on a single page. If there are more than 40 images, we create multiple # pages. The subsiquent pages are given the same name append with a number # (ex: 'pages\savoy_thumb.gon', 'pages\savoy_thumb2.gon', ...) # # When there is more than one page, we create a simple navigation block # which lists the page numbers as links except for the current page which # is bold. For example: # # Page 1 | Page 2 | Page 3 # # ------------- # # This script has another entry point called convertNameIntoTitle. This # entry point is used by the makePage.py script to convert filenames into # page titles. For example: # # convertNameIntoTitle('donnaWedding99') -> 'Donna Wedding 99' # import os import re import sys import glob import string import traceback import Image maxRow = 5 maxPage = 40 MyError = 'MyError' #--------------------------------------------------------------------------- def run(baseDir,outName=None,pageTitle=None,textLink=None): # remove the trailing slash if present if baseDir[-1:] == '\\': baseDir = baseDir[:-1] # Compute the defaults for the parameters if outName==None: outName = baseDir+'_thumb.gon' if pageTitle==None: pageTitle = convertNameIntoTitle(getBaseName(baseDir))+' Pictures' if textLink==None: if os.access(baseDir+'.go',0) or os.access(baseDir+'.gon',0): textLink = getBaseName(baseDir)+'.html' else: textLink = '' # Get a list of all the source images. We ignore any files which do not # begin with a digit or an underscore to make sure that we do not find # previously created thumbnails and scaled images. images = glob.glob(baseDir+'\\[0-9_]*.jpg') if not len(images): raise MyError,'No properly named images were found in the directory '+baseDir # Sort the image list to make sure that the images are in date order. # Note that this will not properly handle directories which wrap across # between the year 1999 and the year 2000. images.sort() # Before we proceed, we parse each image filename. We parse first to make # sure that the filenames are in the proper format. When we parse an image # filename we return a tuple which contains the following fields: # (0) base image name ('990604_1949_BethPlaying.jpg') # (1) image date/time ('Jun 4, 7:49pm') # (2) image title ('Beth Playing') newImages = [] for image in images: baseName = image[len(baseDir)+1:] if hasDateTime(baseName): dateTime = formatDateTime(baseName) title = convertNameIntoTitle(baseName[12:-4]) elif hasDateOnly(baseName): dateTime = formatDateOnly(baseName) title = convertNameIntoTitle(baseName[7:-4]) elif baseName[0] == '_': dateTime = '' title = convertNameIntoTitle(baseName[1:-4]) else: dateTime = '' title = convertNameIntoTitle(baseName[0:-4]) newImages.append( (baseName,dateTime,title) ) images = newImages # Compute the relative directory path to the images. This will throw # an exception of there is no relative path; for example, if the images # are located on a different disk drive than the output file. relDir = computeRelative(baseDir,outName) # Here we create the names for the thumbnail pages (there will be more # than one when there are more than 40 images). We create an array # called outNames which contains the names of each page. # # outNames[0] is an empty string. # outNames[1] is the name of the first page (savoy_thumb.gon) # outNames[2] is the name of the second page (savoy_thumb2.gon) # ... # outNames[N] is the name of the last page # outNames[N+1] is an empty string. # # We also create a parallel array with the htmlNames imageCount = len(images) numPages = (imageCount+maxPage-1)/maxPage lastDot = string.rfind(outName,'.') if lastDot == -1: raise MyError,'No extension found on output filename' outNameN = outName[:lastDot] + '%d' + outName[lastDot:] htmlName = getBaseName(outName[:lastDot]) + '.html' htmlNameN = getBaseName(outName[:lastDot]) + '%d' + '.html' outNames = ['',outName] htmlNames = ['',htmlName] for page in range(2,numPages+1): outNames.append(outNameN % page) htmlNames.append(htmlNameN % page) outNames.append('') htmlNames.append('') # Now we create a thumbnail and scale image for every image in the list # of images. count = len(images) print 'Making thumbnails for %s... %5d left' % (getBaseName(baseDir),count), for image in images: makeSmallJpg(baseDir,image[0]) if not oneSizeOnly(baseDir+'\\'+image[0]): makeLargeJpg(baseDir,image[0]) count = count - 1 print '\b\b\b\b\b\b\b\b\b\b\b%5d left' % count, print '\b\b\b\b\b\b\b\b\b\b\b ' # When we create the html, we create one page for each 42 images. for pageNum in range(1,numPages+1): outFile = open(outNames[pageNum],'w') insertHeader(outFile,pageNum,numPages,pageTitle,textLink,htmlNames) if pageNum == numPages: createTable(outFile,images,baseDir,relDir) else: createTable(outFile,images[:maxPage],baseDir,relDir) images = images[maxPage:] insertFooter(outFile,pageNum,numPages,pageTitle,textLink,htmlNames) print 'Finished - Created %s with %d images on %d page(s)' % (getBaseName(outName),imageCount,pageNum) return outNames[1:-1] #--------------------------------------------------------------------------- # Returns the filename part of a complete path. def getBaseName(pathName): return pathName[string.rfind(pathName,'\\')+1:] #--------------------------------------------------------------------------- # test for a valid date/time or just date in gould standard format. testPat1 = re.compile(r'[0-9]{6,6}_[0-9]{4,4}_[^\.]') testPat2 = re.compile(r'[0-9]{6,6}_[^\.]') def hasDateTime(image): if testPat1.match(image): return 1 else: return 0 def hasDateOnly(image): if testPat2.match(image): return 1 else: return 0 #--------------------------------------------------------------------------- # Return TRUE if the image is already so small that we do not need a # scaled version of the image def oneSizeOnly(fileName): im = Image.open(fileName) if im.size[0] <= 700 and im.size[1] <= 440: return 1 else: return 0 #--------------------------------------------------------------------------- # We return a string which is the date and time that a picture was taken, # extracted from the picture name. A sample date and time string is: # # May 31, 2:05pm monthNames = ('','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec') def formatDateTime(imageName): year = imageName[0:2] month = imageName[2:4] day = int(imageName[4:6]) hour = int(imageName[7:9]) minute = imageName[9:11] if hour > 12: hour,dayHalf=hour-12,'pm' else: dayHalf='am' return monthNames[int(month)]+' '+repr(day)+', '+repr(hour)+':'+minute+dayHalf def formatDateOnly(imageName): year = imageName[0:2] month = imageName[2:4] day = int(imageName[4:6]) if year[0] == '9': year = '19'+year else: year = '20'+year return monthNames[int(month)]+' '+repr(day)+', '+year #--------------------------------------------------------------------------- # We return a string which is the title of the image. We start with the # text which follows the date and add blanks before every capitalized # letter. Then we lowercase any words which should be lowercase in book # titles. lowerWords = [ 'on', 'the', 'from', 'and', 'down', 'up', 'of', 'in', 'off', 'under', 'behind', 'with', 'over', 'below', 'near', 'at', 'out', 'above', 'along', 'for' ] def convertNameIntoTitle(fileName): # the first character is always uppercase even if the image name does not # captialize it title = string.upper(fileName[0]) # copy one character at a time, adding a space instead of any underscore # and before any capital letter or number prevLetter = fileName[0] for letter in fileName[1:]: if letter == '_': title = title + ' ' elif letter in string.uppercase: title = title + ' ' + letter elif letter in string.digits and prevLetter not in string.digits: title = title + ' ' + letter elif letter not in string.digits and prevLetter in string.digits: title = title + ' ' + letter else: title = title + letter prevLetter = letter # Now lowercase the special title words. We do this by converting the # title into an array of words and then back into a string. words = string.split(title) for i in range(1,len(words)): lowerForm = string.lower(words[i]) if lowerForm in lowerWords: words[i] = lowerForm else: words[i] = string.capitalize(words[i]) title = string.join(words) return title #--------------------------------------------------------------------------- # These make the thumbnail and scaled versions of a full size image. def makeSmallJpg(filePath,fileName): im = Image.open(filePath+'\\'+fileName) im.thumbnail((100,100)) im.save(filePath+'\\s'+fileName) def makeLargeJpg(filePath,fileName): im = Image.open(filePath+'\\'+fileName) im.thumbnail((700,440)) im.save(filePath+'\\l'+fileName) #--------------------------------------------------------------------------- # Compute the relative path which gets us from the output html file to # the images. For example: # # baseDir = 'c:\GouldHome\images\savoy' # outName = 'c:\GouldHome\pages\savoy.htm' # => # relDir = '../../images/savoy def computeRelative(baseDir,outName): # The filenames will either begin with a drive letter or with a # double slash. Make sure these strings match. if baseDir[0:2] == '\\\\': baseDir = baseDir[2:] outName = outName[2:] elif baseDir[1] == ':': if baseDir[0:3] != outName[0:3]: raise MyError,'The images and the output file must be located on the same disk' baseDir = baseDir[3:] outName = outName[3:] else: raise MyError,'The input directory must be fully specified' # Separate out all the intermediate directory names. Remember to drop # the actual filename at the end of outName. basePath = string.split(baseDir,'\\') outPath = string.split(outName,'\\')[:-1] # Skip over every directory name which is the same in both paths while len(basePath) and len(outPath) and basePath[0] == outPath[0]: basePath = basePath[1:] outPath = outPath[1:] # The final directory will have a ".." for every remaining directory # in the outPath list, plus the name of every directory in the basePath relDir = string.join( ['..',]*len(outPath)+basePath, '/' ) if relDir == '': relDir = '.' return relDir #--------------------------------------------------------------------------- # Create a thumbnail table def createTable(outFile,images,baseDir,relDir): # we make sure that we put no more than N pictures across on a page, to # ensure this we take our one level array of pictures and turn it into # a two level array of rows of pictures rows = [] count = maxRow for image in images: if count == maxRow: lastRow = [] rows.append(lastRow) count = 0 lastRow.append(image) count = count + 1 for row in rows: outFile.write('
\n') # first we write the images outFile.write('\n') for image in row: width,height = getImageSize(baseDir+'\\s'+image[0]) outFile.write('\n') outFile.write('\n') # then we write the captions outFile.write('\n') for image in row: outFile.write('\n') outFile.write('\n') outFile.write('
\n') if oneSizeOnly(baseDir+'\\'+image[0]): outFile.write('\n' % (relDir+'\\'+image[0])) else: outFile.write('\n' % (relDir+'\\l'+image[0])) outFile.write('\n' % (relDir+'\\s'+image[0],width,height)) outFile.write('
\n') outFile.write(''+image[1]+'
\n') outFile.write(image[2]+'
\n') outFile.write(formatSize(baseDir,relDir,image[0])+'
\n') outFile.write('
\n') #--------------------------------------------------------------------------- # Returns a tuple which is the width and height of a bitmap def getImageSize(fileName): im = Image.open(fileName) return im.size #--------------------------------------------------------------------------- # This returns a string which is the third line of a picture caption. On # the screen, the string prints like a pair of file sizes: # # (45Kb) (133Kb) # # The first size is the size of the full screen (large) image and it links # to the large image file. # # The second size is the size of the original image and it links to the # original image file. # # Note: If the image has only one size, we only return one size/ def formatSize(baseDir,relDir,fileName): smallName = '\\l'+fileName largeName = '\\'+fileName if oneSizeOnly(baseDir+largeName): return '(%dKb)' % (relDir+largeName,fileSize(baseDir+largeName)) else: return '(%dKb) (%dKb)' % (relDir+smallName,fileSize(baseDir+smallName),relDir+largeName,fileSize(baseDir+largeName)) def fileSize(fileName): rawSize = os.stat(fileName)[6] return (rawSize+512)/1024 #--------------------------------------------------------------------------- # This creates a navigation block which looks like: # # Page 1 | Page 2 | Page 3 # # Where the current page is bold and the other pages are links. def insertNavigation(outFile,pageNum,numPages,htmlNames): for page in range(1,numPages+1): if page != 1: outFile.write(' | ') if page == pageNum: outFile.write('Page %d'%page) else: outFile.write('Page %d'%(htmlNames[page],page)) #--------------------------------------------------------------------------- # Inserts the html code for the page header. def insertHeader(outFile,pageNum,numPages,pageTitle,textLink,htmlNames): if numPages > 1: pageTitle = '%s - Page %d' % (pageTitle,pageNum) pageWidth = 113*maxRow if textLink: textLink = 'Return to Text' % textLink outFile.write(pageHeader % (pageWidth,pageTitle,textLink) ) if numPages > 1: insertNavigation(outFile,pageNum,numPages,htmlNames) outFile.write('\n') pageHeader=""" ' % (htmlNames[pageNum-1],pageNum-1) if pageNum == numPages: nextPageRef = '' else: nextPageRef = '' % (htmlNames[pageNum+1],pageNum+1) if textLink: textLink = 'Return to Text' % textLink outFile.write(pageFooter % (113*maxRow,prevPageRef,nextPageRef,textLink)) pageFooter = """

%s

%s
""" #--------------------------------------------------------------------------- # Inserts the html code for the page footer. def insertFooter(outFile,pageNum,numPages,pageTitle,textLink,htmlNames): if pageNum == 1: prevPageRef = '' else: prevPageRef = 'Back to Page %dGoto Page %d
%s%s
%s
""" #--------------------------------------------------------------------------- if __name__=='__main__': try: if len(sys.argv) < 2: print 'Run this program by typing the following command at a DOS prompt:' print ' makeThumb.py ' else: argv = sys.argv[1:] argv.append(None) argv.append(None) argv.append(None) run(argv[0],argv[1],argv[2],argv[3]) except MyError: print 'Error:' print sys.exc_value except: traceback.print_exc() raw_input('Press Enter to exit')