addresses #7
 * all functionality that is available in btn4ws.pl 0.6 is available now
 * Makefile uses a variable for gimptool now
 * compatibility with GIMP 2.4
This commit is contained in:
Jan Dittberner 2007-12-04 13:58:29 +00:00
parent c176a3d901
commit d87ff5223a
3 changed files with 438 additions and 9 deletions

View file

@ -1,3 +1,10 @@
2007-12-04 Jan Dittberner <jan@dittberner.info>
* btn4ws.py: add the functionality available in btn4ws.pl 0.6
* Makefile (GIMPTOOL, clean, dist): use setup.py to build
distribution, use find for clean, create a variable for gimptool
2007-12-03 Jan Dittberner <jan@dittberner.info> 2007-12-03 Jan Dittberner <jan@dittberner.info>
* MANIFEST.in: add AUTHORS, ChangeLog and COPYING to distributed * MANIFEST.in: add AUTHORS, ChangeLog and COPYING to distributed

View file

@ -21,12 +21,14 @@
# version: $Id$ # version: $Id$
# #
GIMPTOOL = gimptool-2.0
install: install:
install -d $(PREFIX)/$$(gimptool --gimpplugindir)/plug-ins install -d $(PREFIX)/$$($(GIMPTOOL) --gimpplugindir)/plug-ins
install btn4ws.py $(PREFIX)/$$(gimptool --gimpplugindir)/plug-ins install btn4ws.py $(PREFIX)/$$($(GIMPTOOL) --gimpplugindir)/plug-ins
install-user: install-user:
gimptool --install-bin btn4ws.py $(GIMPTOOL) --install-bin btn4ws.py
clean: clean:
find . -type f -name '*~' -exec rm {} \; find . -type f -name '*~' -exec rm {} \;

432
btn4ws.py
View file

@ -29,7 +29,7 @@ port of the older gimp-perl version to python.
(c) 2007 Jan Dittberner <jan@dittberner.info> (c) 2007 Jan Dittberner <jan@dittberner.info>
""" """
import logging, sys import os, urllib, logging, sys
from gimpfu import * from gimpfu import *
btn4ws_version = "0.7.0" btn4ws_version = "0.7.0"
@ -38,20 +38,439 @@ logging.basicConfig(level=logging.INFO,
format='$(asctime)s %(levelname)s %(message)s', format='$(asctime)s %(levelname)s %(message)s',
stream=sys.stderr) stream=sys.stderr)
class text_to_name_mapper:
"""
Text string to name mapper class. This class provides mappings for several target
environments.
"""
def __init__(self, strings):
self.mapping = {}
for string in strings: self.mapping[string] = {}
self.idnum = 1
logging.debug("self.mapping=" + str(self.mapping))
def asitemid(self, text):
"""
Get a img itemid for the given text.
"""
if 'itemid' not in self.mapping[text]:
self.mapping[text]['itemid'] = "id%03d" % (self.idnum)
self.idnum += 1
logging.debug("self.mapping=" + str(self.mapping))
return self.mapping[text]['itemid']
def asjavascriptid(self, text):
"""
Get a javascript itemid for the given text.
"""
if 'jsid' not in self.mapping[text]:
self.mapping[text]['jsid'] = "img%03d" % (self.idnum)
self.idnum += 1
logging.debug("self.mapping=" + str(self.mapping))
return self.mapping[text]['jsid']
def aslinktarget(self, text):
"""
Get a link target for the given text.
"""
if 'link' not in self.mapping[text]:
self.mapping[text]['link'] = urllib.quote(text)
logging.debug("self.mapping=" + str(self.mapping))
return "%s.html" % (self.mapping[text]['link'])
def asfilename(self, text, extension = 'png', prefix= '', dirname = None):
"""
Get a filename for the given text with optional extension, prefix and dirname.
"""
if 'file' not in self.mapping[text]:
self.mapping[text]['file'] = text.encode('ascii', 'ignore')
fname = "%s%s.%s" % (prefix, self.mapping[text]['file'], extension)
logging.debug("self.mapping=" + str(self.mapping))
if dirname:
return os.path.join(dirname, fname)
return fname
class text_to_name_mapper:
"""
Text string to name mapper class. This class provides mappings for
several target environments.
"""
def __init__(self, strings):
self.mapping = {}
for string in strings: self.mapping[string] = {}
self.idnum = 1
logging.debug("self.mapping=" + str(self.mapping))
def asitemid(self, text):
"""
Get a img itemid for the given text.
"""
if 'itemid' not in self.mapping[text]:
self.mapping[text]['itemid'] = "id%03d" % (self.idnum)
self.idnum += 1
logging.debug("self.mapping=" + str(self.mapping))
return self.mapping[text]['itemid']
def asjavascriptid(self, text):
"""
Get a javascript itemid for the given text.
"""
if 'jsid' not in self.mapping[text]:
self.mapping[text]['jsid'] = "img%03d" % (self.idnum)
self.idnum += 1
logging.debug("self.mapping=" + str(self.mapping))
return self.mapping[text]['jsid']
def aslinktarget(self, text):
"""
Get a link target for the given text.
"""
if 'link' not in self.mapping[text]:
self.mapping[text]['link'] = urllib.quote(text)
logging.debug("self.mapping=" + str(self.mapping))
return "%s.html" % (self.mapping[text]['link'])
def asfilename(self, text, extension = 'png', prefix= '', dirname = None):
"""
Get a filename for the given text with optional extension,
prefix and dirname.
"""
if 'file' not in self.mapping[text]:
self.mapping[text]['file'] = text.encode('ascii', 'ignore')
fname = "%s%s.%s" % (prefix, self.mapping[text]['file'], extension)
logging.debug("self.mapping=" + str(self.mapping))
if dirname:
return os.path.join(dirname, fname)
return fname
def python_btn4ws(filename, outdir, font, strcolor, transparency, bgcolor, def python_btn4ws(filename, outdir, font, strcolor, transparency, bgcolor,
glow, glowcolor, usepattern, pattern, buttoncolor, glow, glowcolor, usepattern, pattern, buttoncolor,
roundradius, padding, glowsize, bevelwidth, nova, roundradius, padding, glowsize, bevelwidth, nova,
novasparkles, novaradius, novacolor, writexcf, makeinactive, novasparkles, novaradius, novacolor, writexcf, makeinactive,
makeactive, makepressed, makejscript): makeactive, makepressed, makejscript):
""" """
This function controls the creation of the buttons and is registered as This function controls the creation of the buttons and is
gimp plugin. registered as gimp plugin.
""" """
pass # import used gimp pdb functions
createtext = gimp.pdb['gimp_text_fontname']
selectionlayeralpha = gimp.pdb['gimp_selection_layer_alpha']
selectionfeather = gimp.pdb['gimp_selection_feather']
bucketfill = gimp.pdb['gimp_edit_bucket_fill']
selectionall = gimp.pdb['gimp_selection_all']
editclear = gimp.pdb['gimp_edit_clear']
rectselect = gimp.pdb['gimp_rect_select']
ellipseselect = gimp.pdb['gimp_ellipse_select']
selectionshrink = gimp.pdb['gimp_selection_shrink']
selectionnone = gimp.pdb['gimp_selection_none']
fill = gimp.pdb['gimp_edit_fill']
bumpmap = gimp.pdb['plug_in_bump_map']
novaplugin = gimp.pdb['plug_in_nova']
xcfsave = gimp.pdb['gimp_xcf_save']
pngsave = gimp.pdb['file_png_save']
dupimage = gimp.pdb['gimp_image_duplicate']
def parsefont(font):
"""
Parses a font into its fontname and size parts.
"""
parts = font.split(" ")
return (" ".join(parts[:-1]), parts[-1])
def gimp2html_color(color):
"""
Converts a color tuple to a hex encoded color for CSS.
"""
return "#%02x%02x%02x" % (color[0], color[1], color[2])
def toprocess(item):
"""
Decides whether the plugin is able to process the item or not.
"""
item = item.strip()
return len(item) > 0 and not item.startswith('#')
def getmaxextents(strings, fontsize, fontname):
"""
Gets the maximum width and height of texts in strings array
with the given font.
"""
getextents = gimp.pdb['gimp_text_get_extents_fontname']
maxx = 0
maxy = 0
for extents in [getextents(string, fontsize, 1, fontname)
for string in strings]:
maxx = max(maxx, extents[0])
maxy = max(maxy, extents[1])
return (maxx, maxy)
def writejs(dirname, strings):
buf = [
"//",
"// JavaScript generated by btn4ws version %s" % (btn4ws_version),
"//",
"",
"// function to show image for given image_object",
"function hilite(ObjID, imgObjName) {",
" ObjID.src = eval(imgObjName + '.src');",
" return true;",
"}",
""
]
for item in strings:
for prefix in ('a_', 'i_', 'p_'):
buf.append(
"%(prefix)s%(jsid)s = new Image(%(width)d, %(height)d); "
"%(prefix)s%(jsid)s.src = '%(fname)s';"
% {
'prefix' : prefix,
'jsid' : t2nm.asjavascriptid(item),
'width' : width,
'height' : height,
'fname' : urllib.quote(t2nm.asfilename(item, 'png',
prefix))})
jsfile = open(os.path.join(dirname, 'imgobjs.js'), 'w')
jsfile.write("\n".join(buf))
jsfile.close()
def writecss(dirname):
buf = [
"html, body { background-color:%s; }" % (gimp2html_color(bgcolor)),
"a img { border-width: 0; }"
]
cssfile = open(os.path.join(dirname, 'format.css'), 'w')
cssfile.write("\n".join(buf))
cssfile.close()
def writehtml(dirname, strings):
buf = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"',
' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">',
'<head>',
' <title>A JavaScript MouseOver Example</title>',
' <script src="imgobjs.js" type="text/javascript"></script>',
' <link rel="stylesheet" type="text/css" href="format.css"/>',
'</head>',
'<body>',
' <div>'
]
for item in strings:
buf.append(
'<a href="%(target)s"'
' onmouseover="return hilite(%(imgid)s, \'a_%(jsid)s\');"'
' onmouseout="return hilite(%(imgid)s, \'i_%(jsid)s\');"'
' onmousedown="return hilite(%(imgid)s, \'p_%(jsid)s\');"'
' onmouseup="return hilite(%(imgid)s, \'a_%(jsid)s\');">'
'<img src="%(fname)s" class="nav" id="%(imgid)s" width="%(width)d" height="%(height)d"'
' alt="%(text)s" /></a><br />' % {
'target' : t2nm.aslinktarget(item),
'imgid' : t2nm.asitemid(item),
'jsid' : t2nm.asjavascriptid(item),
'fname' : urllib.quote(t2nm.asfilename(item, 'png', 'i_')),
'width' : width,
'height' : height,
'text' : item})
buf.extend([
' </div>',
' <p><a href="http://validator.w3.org/check/referer">'
'<img src="http://www.w3.org/Icons/valid-xhtml11" alt="Valid XHTML 1.1!" height="31"'
'width="88" /></a></p>',
'</html>',
''
])
htmlfile = open(os.path.join(outdir, 'example.html'), 'w')
htmlfile.write("\n".join(buf))
htmlfile.close()
def saveaspng(fname, image):
imgcopy = dupimage(image)
if transparency:
imgcopy.merge_visible_layers(CLIP_TO_BOTTOM_LAYER)
else:
imgcopy.flatten()
pngsave(imgcopy, imgcopy.active_layer, fname, fname, False, 9,
False, False, False, False, True)
gimp.delete(imgcopy)
try:
if not os.path.isfile(filename):
logging.error("%s is not a file.", filename)
return
if not outdir:
outdir = os.path.dirname(filename)
if not os.path.isdir(outdir):
logging.error("%s is not a directory.", outdir)
return
except OSError, e:
logging.error(e)
return
gimp.progress_init()
stringfile = open(filename)
strings = [line.strip()
for line in stringfile.readlines()
if toprocess(line)]
stringfile.close()
t2nm = text_to_name_mapper(strings)
(fontname, fontsize) = parsefont(font)
(maxx, maxy) = getmaxextents(strings, fontsize, fontname)
logging.debug("fontname: %s, fontsize: %d, maxx: %d, maxy: %d",
fontname, int(fontsize), maxx, maxy)
width = maxx + (padding*4)
height = maxy + (padding*4)
logging.debug("width: %d, height: %d", width, height)
if roundradius > height/2:
roundradius = height/2 - 1
if roundradius > width/2:
roundradius = width/2 - 1
logging.debug("roundradius: %d", roundradius)
for text in strings:
image = gimp.Image(width, height, RGB)
image.disable_undo()
gimp.set_foreground(strcolor)
textlayer = createtext(image, None, padding*2, padding*2, text, 0, 1,
fontsize, 1, fontname)
# center the text
textlayer.set_offsets((image.width - textlayer.width)/2 - 1,
(image.height - textlayer.height)/2 - 1)
textlayer.lock_alpha = True
textlayer.name = text
if glow:
texteffect = textlayer.copy(True)
image.add_layer(texteffect, len(image.layers))
offs = texteffect.offsets
texteffect.resize(image.width, image.height, offs[0], offs[1])
texteffect.lock_alpha = False
image.active_layer = texteffect
selectionlayeralpha(texteffect)
selectionfeather(image, glowsize)
gimp.set_foreground(glowcolor)
bucketfill(texteffect, FG_BUCKET_FILL, NORMAL_MODE, 100, 0, True,
0, 0)
btnlayer0 = gimp.Layer(image, "Background", width, height, RGBA_IMAGE,
100, NORMAL_MODE)
image.add_layer(btnlayer0, len(image.layers))
selectionall(image)
editclear(btnlayer0)
offs = btnlayer0.offsets
rectselect(image, offs[0] + roundradius, offs[1],
btnlayer0.width - roundradius*2, btnlayer0.height,
CHANNEL_OP_REPLACE, 0, 0)
rectselect(image, offs[0], offs[1] + roundradius,
btnlayer0.width, btnlayer0.height - roundradius*2,
CHANNEL_OP_ADD, 0, 0)
ellipseselect(image, offs[0], offs[1],
roundradius*2, roundradius*2,
CHANNEL_OP_ADD, False, 0, 0)
ellipseselect(image, offs[0] + btnlayer0.width - roundradius*2,
offs[1],
roundradius*2, roundradius*2,
CHANNEL_OP_ADD, False, 0, 0)
ellipseselect(image, offs[0],
offs[1] + btnlayer0.height - roundradius*2,
roundradius*2, roundradius*2,
CHANNEL_OP_ADD, False, 0, 0)
ellipseselect(image, offs[0] + btnlayer0.width - roundradius*2,
offs[1] + btnlayer0.height - roundradius*2,
roundradius*2, roundradius*2,
CHANNEL_OP_ADD, False, 0, 0)
selectionshrink(image, 1)
selectionfeather(image, 2)
if usepattern:
gimp.pdb['gimp_context_set_pattern'](pattern)
bucketfill(btnlayer0, PATTERN_BUCKET_FILL, NORMAL_MODE, 100, 0,
True, 0, 0)
else:
gimp.set_background(buttoncolor)
bucketfill(btnlayer0, BG_BUCKET_FILL, NORMAL_MODE, 100, 0, True,
0, 0)
selectionnone(image)
selectionlayeralpha(btnlayer0)
selectionfeather(image, 2)
bumplayer = gimp.Layer(image, "Bumpmap", width, height, RGBA_IMAGE,
100, NORMAL_MODE)
gimp.set_background(0, 0, 0)
image.add_layer(bumplayer, 0)
fill(bumplayer, BACKGROUND_FILL)
for index in range(1, bevelwidth -1):
greyness = index*255/bevelwidth;
gimp.set_background(greyness, greyness, greyness)
bucketfill(bumplayer, BG_BUCKET_FILL, NORMAL_MODE, 100, 0, False,
0, 0)
selectionshrink(image, 1)
gimp.set_background(255, 255, 255)
bucketfill(bumplayer, BG_BUCKET_FILL, NORMAL_MODE, 100, 0, False, 0, 0)
selectionnone(image)
btnlayer1 = btnlayer0.copy(True)
btnlayer2 = btnlayer0.copy(True)
image.add_layer(btnlayer1, len(image.layers))
image.add_layer(btnlayer2, len(image.layers))
bumpmap(image, btnlayer1, bumplayer, 125, 45, 3, 0, 0, 0, 0, 0, 0, 1)
bumpmap(image, btnlayer2, bumplayer, 125, 45, 3, 0, 0, 0, 0, 0, 1, 1)
image.remove_layer(bumplayer)
#gimp.delete(bumplayer)
if nova:
novalayer = gimp.Layer(image, "Nova", width, height, RGBA_IMAGE,
75, NORMAL_MODE)
image.add_layer(novalayer, 0)
selectionall(image)
image.active_layer = novalayer
editclear(novalayer)
selectionnone(image)
novaplugin(image, novalayer, width/4, height/4,
novacolor, novaradius, novasparkles, 0)
blackboard = gimp.Layer(image, "Blackboard", width, height, RGBA_IMAGE,
100, NORMAL_MODE)
image.add_layer(blackboard, len(image.layers))
selectionall(image)
if transparency:
blackboard.preserve_trans = True
editclear(blackboard)
else:
gimp.set_background(bgcolor)
bucketfill(blackboard, BG_BUCKET_FILL, NORMAL_MODE, 100, 0, False,
0, 0)
selectionnone(image)
if writexcf:
fname = t2nm.asfilename(text, 'xcf', dirname = outdir)
xcfsave(0, image, textlayer, fname, fname)
if makepressed:
btnlayer0.visible = False
btnlayer1.visible = False
btnlayer2.visible = True
if nova: novalayer.visible = True
saveaspng(t2nm.asfilename(text, 'png', 'p_', outdir), image)
if makeactive:
btnlayer0.visible = False
btnlayer1.visible = True
btnlayer2.visible = False
if nova: novalayer.visible = True
saveaspng(t2nm.asfilename(text, 'png', 'a_', outdir), image)
if makeinactive:
btnlayer0.visible = True
btnlayer1.visible = False
btnlayer2.visible = False
if nova: novalayer.visible = False
saveaspng(t2nm.asfilename(text, 'png', 'i_', outdir), image)
image.enable_undo()
#gimp.Display(image)
gimp.progress_update((strings.index(text)+1)/len(strings))
gimp.delete(image)
if makejscript:
writejs(outdir, strings)
writecss(outdir)
writehtml(outdir, strings)
#gimp.displays_flush()
register( register(
"python_fu_btn4ws", "Buttons for website", "python_fu_btn4ws", "Buttons for website",
"Creates buttons for a website. Which have the same size, layout, effects on it. It's possible to create JavaScript code, CSS and XHTML examples for MouseOver effects also.", """Creates buttons for a website. Which have the same size,
layout, effects on it. It's possible to create JavaScript code,
CSS and XHTML examples for MouseOver effects also.""",
"Jan Dittberner", "Jan Dittberner",
"Jan Dittberner <jan@dittberner.info>", "Jan Dittberner <jan@dittberner.info>",
"%s, %s" % (btn4ws_version, "%s, %s" % (btn4ws_version,
@ -63,7 +482,8 @@ register(
(PF_FONT, "font", "Font for the strings", "Sans 18"), (PF_FONT, "font", "Font for the strings", "Sans 18"),
(PF_COLOR, "string_color", "Color of the strings", (255, 255, 0)), (PF_COLOR, "string_color", "Color of the strings", (255, 255, 0)),
(PF_TOGGLE, "transparent_background", (PF_TOGGLE, "transparent_background",
"Keep the background transparent (This doesn't work in MS Internet Explorer <= 6.0)", 0), """Keep the background transparent (This doesn't work in MS
Internet Explorer <= 6.0)""", 0),
(PF_COLOR, "background_color", "Color of the background", (7, 135, 255)), (PF_COLOR, "background_color", "Color of the background", (7, 135, 255)),
(PF_TOGGLE, "apply_glow", "Enable glow effect", 1), (PF_TOGGLE, "apply_glow", "Enable glow effect", 1),
(PF_COLOR, "glow_color", "Color of the Glow effect", (255, 180, 0)), (PF_COLOR, "glow_color", "Color of the Glow effect", (255, 180, 0)),