#!/usr/pkg/bin/python """windows .cur (cursor) to html table displayer and editor an attempt to create and edit .cur files online born out of desperation when trying to use ImageMagick's 'convert' program to convert a cursor file to BMP so I could modify it, then change it back to a cursor. it always changed it to a format 1 image the same as an .ico file, and IE6 wouldn't accept it as a cursor. not saying it can't be done, but i gave up on it""" Copyright = """ cur2html -- convert a cursor to an html table (and back again?) Copyright (C) 2004 John Comeau 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., 675 Mass Ave, Cambridge, MA 02139, USA. """ errormessage = "Not all needed libraries found, upgrade or check path" try: True # not defined in older Python releases except: True, False = 1, 0 try: import sys, os, types, re, pwd sys.path.append(os.sep.join([pwd.getpwuid(os.geteuid())[5], 'lib', 'python'])) from com.jcomeau import gpl, jclicense except: try: sys.stderr.write("%s\n" % errormessage) except: print errormessage raise # get name this program was called as self = sys.argv[0].split(os.sep)[-1] command = self.split('.')[0] # chop any suffix (extension) # now get name we gave it when we wrote it originalself = Copyright.split()[0] # globals and routines that should be in every program # (yes, you could import them, but there are problems in that approach too) def DebugPrint(*whatever): return False # defined instead by pytest module, use that for debugging # other globals, specific to this program abbreviation = '01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw' def cur2txt(*args): """convert cursor file to text representation looks in argv for input and output filenames if no output filename, outputs to stdout if input filename is "-", expects input from stdin """ maxsize = 4096 # arbitrary, I just like this number :^) try: while type(args[0]) != types.StringType: args = args[0] filename = args[0] except: raise try: warnings = [] if filename != '-': if os.stat(filename)[6] > maxsize: raise Exception, "file size too large" filehandle = open(filename, 'rb') else: filehandle = sys.stdin try: outfilename = args[1] except: outfilename = '-' if outfilename != '-': outfilehandle = open(outfilename, 'wb') else: outfilehandle = sys.stdout except: raise try: contents = filehandle.read(maxsize) # slurp whole file in # not trying for endian-independence here, feel free to do so... magic = contents[0:4] if magic != '\x00\x00\x02\x00': raise Exception, "not recognizable as a cursor file: %s" % repr(magic) count = short(contents[4:]) if count == 0: raise Exception, "no cursors found in %s" % filename if count > 0: warnings.append("more than one cursor found, only using first one") width, height = ord(contents[6]), ord(contents[7]) DebugPrint('dimensions:', width, height) colors, reserved = ord(contents[8]), ord(contents[9]) if colors != 0 or reserved != 0: warnings.append("strange values found for colors/reserved: %d, %d" % (colors, reserved)) hotspotX, hotspotY = short(contents[10:]), short(contents[12:]) DebugPrint('hotspot at:', hotspotX, hotspotY) bytesize = long(contents[14:]) DebugPrint('size of infoheader + AND bitmap + XOR bitmap: ', bytesize) infoheader = long(contents[18:]) DebugPrint('infoheader starts at offset: ', infoheader) headersize = long(contents[infoheader:]) if headersize != 40: warnings.append("proceeding as if header were 40 bytes, though it is %d" % headersize) cursorwidth = long(contents[infoheader + 4:]) if cursorwidth != width: raise Exception, 'BMP cursor width %d does not match CUR width %d' % \ (cursorwidth, width) cursorheight = long(contents[infoheader + 8:]) if cursorheight != height * 2: raise Exception, 'wrong scan line count %d for height %d, should be %d' % \ (cursorheight, height, height * 2) DebugPrint('cursor dimensions:', cursorwidth, cursorheight) planes = short(contents[infoheader + 12:]) bits = short(contents[infoheader + 14:]) compression = long(contents[infoheader + 16:]) if compression != 0: raise Exception, 'cannot deal with compression type %d' % compression imagesize = long(contents[infoheader + 20:]) DebugPrint('size of image in bytes', imagesize) # skip next 4 longwords: # XPixelsPerM # YPixelsPerM # ColorsUsed # ColorsImportant R, G, B = 0, 1, 2 # offsets of colors, hopefully not too cryptic color = [] pixels = [] colors_used = [] if bits < 24: for index in range(0, 2 << (bits - 1)): offset = infoheader + headersize + (index * 4) color.append(map(ord, contents[offset:offset + 3])) #DebugPrint('color[%d]:' % index, color[index]) print "%d: #%02x%02x%02x" % (index, color[index][R], color[index][G], color[index][B]) pixeldata = infoheader + headersize + ((2 << bits - 1) * 4) else: pixeldata = infoheader + headersize # no color table for bits=24 or bits=32 if bits == 1: raise Exception, 'bpp=1 not yet implemented' elif bits == 2: raise Exception, 'bpp=2 not yet implemented' elif bits == 4: raise Exception, 'bpp=4 not yet implemented' elif bits == 8: rowbytes = int((width + 3) / 4) * 4 # round up to 4-byte boundary dump = DebugPrint('pixel map follows:') for index in range(height - 1, -1, -1): rowstart = pixeldata + (rowbytes * index) pixels.append([]) # start new row for pixel in map(ord, contents[rowstart:rowstart + width]): if dump: sys.stderr.write('%02x' % pixel) # add this color to colors_used array unless it's already there try: colors_used.index(pixel) except: colors_used.append(pixel) # add this pixel to 2-dimensional array pixels[-1].append(pixel) if dump: sys.stderr.write('\n') else: raise Exception, 'bpp=%d not yet implemented' % bits maskdata = pixeldata + (rowbytes * height) maskbytes = (int((width / 8) + 3) / 4) * 4 # mask is always 1 bpp bitindex = 0 dump = DebugPrint('pixelmask map follows:') for index in range(height - 1, -1, -1): rowstart = maskdata + (maskbytes * index) # DebugPrint('mask:', contents[rowstart:rowstart + maskbytes]) for pixeloffset in range(0, width): offset = int(pixeloffset / 8) mask = (ord(contents[rowstart + offset]) & (128 >> (pixeloffset % 8))) / \ (128 >> (pixeloffset % 8)) if mask > 0: pixels[(height - 1) - index][pixeloffset] = -1 if dump: sys.stderr.write('%02x' % mask) if dump: sys.stderr.write('\n') DebugPrint('colors_used:', colors_used) DebugPrint('pixels:', pixels) for index in range(0, len(pixels)): for pixeloffset in range(0, len(pixels[index])): pixel = pixels[index][pixeloffset] if pixel == -1: outfilehandle.write(' ') else: outfilehandle.write(abbreviation[colors_used.index(pixel)]) outfilehandle.write('\n') except: raise def cur2html(*args): """convert cursor file to HTML template looks in argv then in CGI parameters" """ maxsize = 4096 # arbitrary, I just like this number :^) try: while type(args[0]) != types.StringType: args = args[0] filename = args[0] except: raise try: warnings = [] if os.stat(filename)[6] > maxsize: raise Exception, "file size too large" filehandle = open(filename, 'rb') except: raise try: contents = filehandle.read() # slurp whole file in # not trying for endian-independence here, feel free to do so... magic = contents[0:4] if magic != '\x00\x00\x02\x00': raise Exception, "not recognizable as a cursor file: %s" % repr(magic) count = short(contents[4:]) if count == 0: raise Exception, "no cursors found in %s" % filename if count > 0: warnings.append("more than one cursor found, only using first one") width, height = ord(contents[6]), ord(contents[7]) DebugPrint('dimensions:', width, height) colors, reserved = ord(contents[8]), ord(contents[9]) if colors != 0 or reserved != 0: warnings.append("strange values found for colors/reserved: %d, %d" % (colors, reserved)) hotspotX, hotspotY = short(contents[10:]), short(contents[12:]) DebugPrint('hotspot at:', hotspotX, hotspotY) bytesize = long(contents[14:]) DebugPrint('size of infoheader + AND bitmap + XOR bitmap: ', bytesize) infoheader = long(contents[18:]) DebugPrint('infoheader starts at offset: ', infoheader) headersize = long(contents[infoheader:]) if headersize != 40: warnings.append("proceeding as if header were 40 bytes, though it is %d" % headersize) cursorwidth = long(contents[infoheader + 4:]) if cursorwidth != width: raise Exception, 'BMP cursor width %d does not match CUR width %d' % \ (cursorwidth, width) cursorheight = long(contents[infoheader + 8:]) if cursorheight != height * 2: raise Exception, 'wrong scan line count %d for height %d, should be %d' % \ (cursorheight, height, height * 2) DebugPrint('cursor dimensions:', cursorwidth, cursorheight) planes = short(contents[infoheader + 12:]) bits = short(contents[infoheader + 14:]) compression = long(contents[infoheader + 16:]) if compression != 0: raise Exception, 'cannot deal with compression type %d' % compression imagesize = long(contents[infoheader + 20:]) DebugPrint('size of image in bytes', imagesize) # skip next 4 longwords: # XPixelsPerM # YPixelsPerM # ColorsUsed # ColorsImportant R, G, B = 0, 1, 2 # offsets of colors, hopefully not too cryptic color = [] if bits < 24: for index in range(0, 2 << (bits - 1)): offset = infoheader + headersize + (index * 4) color.append(map(ord, contents[offset:offset + 3])) DebugPrint('color[%d]:' % index, color[index]) pixeldata = infoheader + headersize + ((2 << bits - 1) * 4) else: pixeldata = infoheader + headersize # no color table for bits=24 or bits=32 if bits == 1: raise Exception, 'bpp=1 not yet implemented' elif bits == 2: raise Exception, 'bpp=2 not yet implemented' elif bits == 4: raise Exception, 'bpp=4 not yet implemented' elif bits == 8: rowbytes = int((width + 3) / 4) * 4 # round up to 4-byte boundary dump = DebugPrint('pixel map follows:') for index in range(0, height): rowstart = pixeldata + (rowbytes * index) if dump: for pixel in map(ord, contents[rowstart:rowstart + width]): sys.stderr.write('%02x' % pixel) sys.stderr.write('\n') else: raise Exception, 'bpp=%d not yet implemented' % bits maskdata = pixeldata + (rowbytes * height) maskbytes = (int((width / 8) + 3) / 4) * 4 # mask is always 1 bpp bitindex = 0 dump = DebugPrint('pixelmask map follows:') for index in range(0, height): rowstart = maskdata + (maskbytes * index) # DebugPrint('mask:', contents[rowstart:rowstart + maskbytes]) for pixeloffset in range(0, width): offset = int(pixeloffset / 8) mask = (ord(contents[rowstart + offset]) & (128 >> (pixeloffset % 8))) / \ (128 >> (pixeloffset % 8)) if dump: sys.stderr.write('%02x' % mask) if dump: sys.stderr.write('\n') except: raise def short(bytestring): "convert first two bytes of bytestring to 16-bit integer" return ord(bytestring[0]) + (256 * ord(bytestring[1])) def long(bytestring): "convert first 4 bytes of bytestring into 32-bit integer" return short(bytestring) + (0x10000 * short(bytestring[2:])) if __name__ == '__main__': # if this program was imported by another, the above test will fail, # and this following code won't be used... eval("%s(sys.argv[1:])" % command) else: # if you want something to be done on import, do it here; otherwise pass pass