#! /python21/python

"""
program: counter.py
creator: Rob Chavers on 4/27/2001
purpose: to show a hit-counter on a web page.
language: Python 2.0 on Win98 platform (should also work with *nix)
          must have PIL(PythonWare Image Library) installed

syntax : <img src="counter.py?page=python.html&digitsfile=57chevy.gif&length=6">
  or
for a random font:
         <img src="counter.py?page=somepage.html&digitsfile=random&length=8">

future: 1) add counter to a database or single text file instead of the current
           "each page has its own counter text file" schema.
"""

import sys, os, string, StringIO
import Image

image_dir = 'c:\html\images\counters'
os.chdir('c:\html\cgi-bin\page_counters')

# make sure no one is using my server
# to store any remote counters.
# Future: a jpg that says thief!
#
referer = string.split(os.environ['HTTP_REFERER'], '/')[2]
ref_list = string.split(referer, '.')
domain = ref_list[-2]

if domain != 'racmar' and domain != 'rchavers':
    sys.exit(0)


# these are the defaults
#
digitsFile = 'random'
length = 6
page = ''

# let the user override the defaults
#
try:
    arg_list = string.split(os.environ['QUERY_STRING'], '&')
    for item in arg_list:
        pair = string.split(item, '=')
        if len(pair) != 2: continue
        key = string.lower(pair[0])
        value = pair[1]
        if key == 'digitsfile': digitsFile = value
        if key == 'page': page = value
        if key == 'length': length = int(value)

    if string.lower(digitsFile) == 'random':
        from random import randrange
        files = os.listdir(image_dir)
        try:
           files.remove('original_site.txt')
        except:
           pass
        digitsFile = files[randrange(0, len(files), 1)]

except:
    pass

# if the web page name was not given, show an error
# Future: a jpg that says error!
#
if not page:
    sys.exit(0)


# open the counter file and add one, if the file is not found
# reset the count to 1 hit
#
try:
    count = int(open('%s.txt' %page, 'r').readline()) + 1
except:
    count = 1

# write the new count to this page's text file
#
open('%s.txt' %page, 'w').write('%d\n' %count)


# make sure we have at least 'length' digits to display by padding zeros on the left
#
str_count = ('0' * length + `count`)[-length:]


# open the gif image of the digits...
#
data = open('%s\%s' %(image_dir, digitsFile), 'rb').read()
im = Image.open(StringIO.StringIO(data))

# get the height and width of the original image
#
im_width = im.size[0]
im_height = im.size[1]

# build a list of pixel coordinate locations that each char starts.
# they are tuples of the form (left, upper, right, lower)
#
boxes = []

# this code is for the variable-length digit counter files...
# I only added this because I *really* wanted to get the sign language
# counter to work properly.  It required a longer program and almost a
# complete re-write of the code! 
# (a tribute to Kevan North's friendship :-) )
#
#
# i don't really know how to correctly determine the starting and
# endng point of the images offset data (found in variable-length
# counter images).  I simply opened my favorite  hex-editor and 
# noticed a pattern of where to start and stop I KNOW this is not 
# the *correct* solution, so if someone can clue me in on the finer
# details of GIF comment blocks, I'll definately listen!
#
start = string.find(data, chr(33) + chr(254))
stop  = string.find(data, chr(0) + chr(44))

if start < stop and (start != -1 and stop != -1):
    parts = string.split(data[start + 3:stop], ':')
    try:
        if int(parts[0]) >= 10: 
            parts = parts[1:]
            for index in range(10):
                boxes.append((int(parts[index]), 0, int(parts[index + 1]), im_height))
    except:
        pass

# ok, we did not find any variable length data, so we will
# assume this image is of uniform length (it can be either
# horizontal or vertical)
#
if not boxes:
    if im_height > im_width:
        h_offset = im_height / 10
        w_offset = im_width
    else:
        h_offset = im_height
        w_offset = im_width / 10
    for index in range(10):
        if im_height > im_width:
            box = (0, index * h_offset, w_offset, index * h_offset + h_offset)
        else:
            box = (index * w_offset, 0, index * w_offset + w_offset, h_offset)
        boxes.append(box)


# for each of the numbers in the string, lets
# find the digit from the source and then
# create the final boxes to show it.  also,
# calculate the actual width of our counter...
#
imout_width = 0
boxes_out = []
for index in range(len(str_count)):
    digit = int(str_count[index])
    box = boxes[digit]
    boxes_out.append(box)
    imout_width = imout_width + box[2] - box[0]


# create a blank new image
#
imout = Image.new("RGB", (imout_width, boxes_out[0][3]))

# fill in the blank image with the digits
# 
offset = 0
for box in boxes_out:
    imout.paste(im.crop(box), (offset, 0))
    offset = offset + box[2] - box[0]


# convert the new (completed!) image into a jpeg 
# and store it as a string to give to the browser
#
jpeg = imout.tostring('jpeg', imout.mode)


# ok, this took me FOREVER (2 days) to find!
# I like python very much, but I program in unix by
# day and win32 by night.  So pyhton on a windows
# computer makes your life "easier" by changing all
# LF('\n') into CRLF('\r\n') (how Microsoft-ish).
# Thus if you wish to output binary data (graphics) 
# on win32 python you should remember this little fix.
#
try:
    import msvcrt
    # this wil set stdout to binary mode on win32 python!
    # use os.O_TEXT to set it back to ascii mode
    #
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
except:
    pass

# finally, give the jpeg to the browser
#
print 'Content-type: image/jpeg'
print 'Content-length: %d\n' %len(jpeg)
print jpeg
