#!/usr/bin/python # # This program copyright (C) 2011 Nicolas Schodet # # All 'awesome' original icons generated by this program were created by # Adrian C. (anrxc). They are licensed under the same terms as the awesome # distribution itself - GNU General Public License version 2. They can be # generated with the options: # # --fg trans --bg '#dcdccc' --size 12x12 --margin_top 1 --margin_right 2 \ # --margin_left -1 --too_small 3 # # Icons generated with other parameters are inspired by those icons. # # # 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. # # Contact : # Web: http://ni.fr.eu.org/ # Email: # """Generate a set of icons to be used with the Awesome Window Manager.""" import copy import optparse import ConfigParser import Image, ImageDraw import os, os.path from fnmatch import fnmatch class Params(object): """Parameters for icons generation, can be loaded with config file or command line arguments.""" def __init__ (self, size, fg, bg, margin_top = 0, margin_bottom = 0, margin_left = 0, margin_right = 0, too_small = 0): """Initialize with provided default values.""" self.size = size self.fg = fg self.bg = bg self.margin_top = margin_top self.margin_bottom = margin_bottom self.margin_left = margin_left self.margin_right = margin_right self.too_small = too_small @property def coords (self): """Get coordinates of the corner points.""" return (self.margin_left, self.margin_top, self.size[0] - self.margin_right - 1, self.size[1] - self.margin_bottom - 1) def update (self, d): """Update from dictionary.""" if 'size' in d and d['size'] is not None: w, h = d['size'].split ('x') self.size = (int (w) if w else None, int (h) if h else None) for n in ('fg', 'bg'): if n in d and d[n] is not None: color = d[n] if color == 'trans': color = (0, 0, 0, 0) setattr (self, n, color) for n in ('margin_top', 'margin_bottom', 'margin_left', 'margin_right', 'too_small'): if n in d and d[n] is not None: setattr (self, n, int (d[n])) def ratio (self, ratio): """Return a Params object, updating size for a given ratio.""" if self.size[0] is None: p = copy.copy(self) p.size = ((p.size[1] * ratio[0] + ratio[1] - 1) / ratio[1], p.size[1]) return p elif self.size[1] is None: p = copy.copy(self) p.size = (p.size[0], (p.size[0] * ratio[1] + ratio[0] - 1) / ratio[0]) return p else: return self def part (a, b, i, n, rounding = 'nearest'): """Return the ith separator position when partitioning the space between a and b. Rounding methods: - nearest: pixel nearest the real value, - down: pixel to the left/above the real value, - up: pixel to the right/below the real value, - center: pixel toward center, - edge: pixel toward edge """ if rounding == 'nearest': offset = n / 2 elif rounding == 'down': offset = 0 elif rounding == 'up': offset = n - 1 elif rounding == 'center': if i <= n / 2: offset = n - 1 else: offset = 0 elif rounding == 'edge': if i <= n / 2: offset = 0 else: offset = n - 1 return a + (i * (b - a) + offset) / n def draw_grid (draw, (x1, y1, x2, y2), nx, ny, rounding, p): """Draw a grid with nx column and ny lines.""" for xi in xrange (0, nx + 1): x = part (x1, x2, xi, nx, rounding) draw.line ((x, y1, x, y2), fill = p.fg) for yi in xrange (0, ny + 1): y = part (y1, y2, yi, ny, rounding) draw.line ((x1, y, x2, y), fill = p.fg) def icon_layouts_max (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords draw.line ((x1, y1, x2, y2), fill = p.fg) draw.line ((x1, y2, x2, y1), fill = p.fg) def icon_layouts_dwindle (icon, draw, p): x1, y1, x2, y2 = p.coords draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) x1 = (x1 + x2) / 2 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) y1 = (y1 + y2) / 2 if (y2 - y1) % 2: y1 += 1 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) x1 = (x1 + x2) / 2 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) y1 = (y1 + y2) / 2 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) def icon_layouts_spiral (icon, draw, p): x1, y1, x2, y2 = p.coords draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) x1 = (x1 + x2) / 2 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) y1 = (y1 + y2) / 2 if (y2 - y1) % 2: y1 += 1 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) x2 = (x1 + x2 + 1) / 2 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) y2 = (y1 + y2) / 2 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) def icon_layouts_fairh (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords y3 = part (y1, y2, 2, 3, 'nearest') ny = 2 if (y3 - y1) / 2 - 1 > p.too_small else 1 draw_grid (draw, (x1, y1, x2, y3), 3, ny, 'nearest', p) draw_grid (draw, (x1, y3, x2, y2), 2, 1, 'nearest', p) def icon_layouts_fairv (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords x3 = part (x1, x2, 2, 3, 'nearest') nx = 2 if (x3 - x1) / 2 - 1 > p.too_small else 1 draw_grid (draw, (x1, y1, x3, y2), nx, 3, 'nearest', p) draw_grid (draw, (x3, y1, x2, y2), 1, 2, 'nearest', p) def icon_layouts_floating (icon, draw, p): x1, y1, x2, y2 = p.coords xl = part (x1, x2, 2, 10, 'down') yl = part (y1, y2, 8, 10, 'up') xs = part (x1, x2, 1, 2, 'up') ys = part (y1, y2, 1, 2, 'down') draw.rectangle ((xl, y1, x2, yl), outline = p.fg, fill = p.bg) draw.rectangle ((x1, ys, xs, y2), outline = p.fg, fill = p.bg) def icon_layouts_fullscreen (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords xl = part (x1, x2, 3, 10, 'down') xr = part (x1, x2, 7, 10, 'up') yt = part (y1, y2, 2, 10, 'down') ym = part (y1, y2, 5, 10, 'down') yb = part (y1, y2, 8, 10, 'up') draw.line ((xl, yb, xl, yt, xr, yt), fill = p.fg) draw.line ((xl, ym, xr, ym), fill = p.fg) def icon_layouts_magnifier (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords xl = part (x1, x2, 2, 10, 'edge') xr = part (x1, x2, 8, 10, 'edge') yt = part (y1, y2, 3, 10, 'edge') yb = part (y1, y2, 7, 10, 'edge') ny = 2 if xl - x1 > p.too_small else 1 nx = 2 if yt - y1 > p.too_small else 1 draw_grid (draw, p.coords, nx, ny, 'nearest', p) draw.rectangle ((xl, yt, xr, yb), outline = p.fg, fill = p.bg) def icon_layouts_tilebottom (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords ym = part (y1, y2, 1, 2, 'up') draw_grid (draw, (x1, ym, x2, y2), 3, 1, 'nearest', p) def icon_layouts_tileleft (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords xm = part (x1, x2, 1, 2, 'down') draw_grid (draw, (x1, y1, xm, y2), 1, 3, 'nearest', p) def icon_layouts_tile (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords xm = part (x1, x2, 1, 2, 'up') draw_grid (draw, (xm, y1, x2, y2), 1, 3, 'nearest', p) def icon_layouts_tiletop (icon, draw, p): draw.rectangle (p.coords, outline = p.fg, fill = p.bg) x1, y1, x2, y2 = p.coords ym = part (y1, y2, 1, 2, 'down') draw_grid (draw, (x1, y1, x2, ym), 3, 1, 'nearest', p) def icon_taglist_sel (icon, draw, p): x1, y1, x2, y2 = p.coords y2 = y1 + x2 - x1 draw.rectangle ((x1, y1, x2, y2), outline = p.fg, fill = p.bg) icon_taglist_sel.ratio = (1, 4) def icon_taglist_unsel (icon, draw, p): x1, y1, x2, y2 = p.coords y2 = y1 + x2 - x1 draw.line ((x1, y2, x1, y1, x2, y1), fill = p.fg) icon_taglist_unsel.ratio = (1, 4) def icon_awesome (icon, draw, p): draw.rectangle (p.coords, outline = p.bg, fill = p.bg) x0, y0, x3, y3 = p.coords x1 = part (x0 - 1, x3 + 1, 1, 3, 'center') x2 = part (x0 - 1, x3 + 1, 2, 3, 'center') y1 = part (y0 - 1, y3 + 1, 1, 3, 'center') y2 = part (y0 - 1, y3 + 1, 2, 3, 'center') draw.line ((x0, y1, x2, y1), fill = p.fg) draw.line ((x1, y2, x2, y2, x2, y3), fill = p.fg) def icon_widget_separator (icon, draw, p): x1, y1, _, y2 = p.coords draw.line ((x1, y1, x1, y2), fill = p.fg) icon_widget_separator.ratio = (1, 4) def icon_widget_bat (icon, draw, p): x1, y1, x2, y2 = p.coords s = part (0, y2 - y1, 1, 10, 'up') bx1 = part (x1, x2, 1, 4, 'center') bx2 = part (x1, x2, 3, 4, 'center') draw.rectangle ((bx1 + 2, y1 + 1, bx2 - 2, y1 + 1 + s), outline = p.fg, fill = p.bg) draw.rectangle ((bx1, y1 + 1 + s, bx2, y2 - 1), outline = p.fg, fill = p.bg) draw.rectangle ((bx1 + 2, y1 + 1 + s + 2, bx2 - 2, y2 - 1 - 2), outline = p.fg, fill = p.fg) icons = dict((f[5:].replace ('_', '/'), globals ()[f]) for f in dir () if f.startswith ('icon_')) for name, func in icons.iteritems (): if not hasattr (func, 'ratio'): func.ratio = (1, 1) parser = optparse.OptionParser (description = __doc__) parser.add_option ('-c', '--config', metavar = 'FILE', action = 'append', help = "generate for all configurations read from config FILE, if" " given, other options will override values from config file") parser.add_option ('-O', '--output-dir', metavar = 'DIR', help = "generate icons below given directory") parser.add_option ('-f', '--fg', metavar = 'COLOR', help = "use COLOR as foreground color") parser.add_option ('-b', '--bg', metavar = 'COLOR', help = "use COLOR as background color") parser.add_option ('-i', '--include', metavar = 'PATTERN', action = 'append', help = 'only include icons matching given shell PATTERN') parser.add_option ('-s', '--size', metavar = 'WIDTHxHEIGHT', help = "use specified size for icons (one length can be missing)") parser.add_option ('--margin-top', metavar = 'PIXELS', type = 'int', help = "keep a margin, define y1") parser.add_option ('--margin-bottom', metavar = 'PIXELS', type = 'int', help = "keep a margin, define y2") parser.add_option ('--margin-left', metavar = 'PIXELS', type = 'int', help = "keep a margin, define x1") parser.add_option ('--margin-right', metavar = 'PIXELS', type = 'int', help = "keep a margin, define x2") parser.add_option ('-S', '--too-small', metavar = 'PIXELS', type = 'int', help = "do not draw some details smaller than a number of PIXELS") parser.add_option ('--list', action = 'store_true', help = "instead of generating icons, print a list of available icons") parser.add_option ('-q', '--quiet', action = 'store_true', help = "suppress verbose messages") (options, args) = parser.parse_args () if args: parser.error ('no argument expected') if options.list: print "\n".join (icons.keys()) else: configs = options.config or [ None ] for config in configs: # Get sections from configuration file, or use dummy section. if config is not None: cp = ConfigParser.SafeConfigParser () cp.readfp (open (config)) sections = cp.sections () else: sections = [ None ] # Output icons for every sections. for section in sections: p = Params ((None, 12), 'white', 'black') output_dir = '.' include = [ '*' ] # Merge config and command line. if section is not None: p.update (dict (cp.items (section))) if cp.has_option (section, 'output_dir'): output_dir = cp.get (section, 'output_dir') if cp.has_option (section, 'include'): include = cp.get (section, 'include').split () p.update (options.__dict__) if options.output_dir is not None: output_dir = options.output_dir if options.include is not None: include = options.include # Generate matching icons. for name, func in icons.iteritems(): for pattern in include: if fnmatch (name, pattern): lp = p.ratio (func.ratio) icon = Image.new ('RGBA', lp.size, (0, 0, 0, 0)) draw = ImageDraw.Draw (icon) if not options.quiet: print "Generating %s..." % name func (icon, draw, lp) del draw filename = os.path.join (output_dir, name + '.png') try: os.makedirs (os.path.dirname (filename)) except OSError: pass icon.save (filename) break