diff options
Diffstat (limited to 'imgrid.py')
| -rwxr-xr-x | imgrid.py | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/imgrid.py b/imgrid.py new file mode 100755 index 0000000..a751b6b --- /dev/null +++ b/imgrid.py @@ -0,0 +1,356 @@ +#!/usr/bin/python2.7 + +import sys +import re +import os +import string +import simplejson as json +import random +import time +import urllib +import urllib2 +from subprocess import Popen,PIPE,call +urlencode = urllib.urlencode +urlopen = urllib2.urlopen +Request = urllib2.Request + +MAX_SIZE = 1024 * 1024 * 1.2 * 1.5 +WORKING_DIR = "/tmp" + +BIN_CONVERT = "/usr/bin/convert" +BIN_COMPOSITE = "/usr/bin/composite" +BIN_IDENTIFY = "/usr/bin/identify" +THREEDROTATE = "./3Drotate" +GRID = "./grid" +DB_TAG = "grid"; + +DEFAULT_HEIGHT = 400 +DEFAULT_WIDTH = 600 +DEFAULT_LINE_COLOR = "silver" +DEFAULT_FINALFORMAT = "png" + +#{{{Utility functions +def bool_correct(s): + if re.match(r'^false$', s, re.IGNORECASE): + return False + elif re.match(r'^true$', s, re.IGNORECASE): + return True + else: + return s + +class dotdict(dict): + """dot.notation access to dictionary attributes""" + def __getattr__(self, attr): + return self.get(attr) + __setattr__= dict.__setitem__ + __delattr__= dict.__delitem__ + +def get_mimetype(f): + try: + mimetype = Popen( + [BIN_IDENTIFY, f], stdout=PIPE + ).communicate()[0].split(" ")[1].lower() + return mimetype + except Exception as e: + sys.stderr.write(str(e)) + raise; + +def sanitize (str): + return re.sub(r'\W+', '', str) + +def now(): + return int(time.time()) + +def browser_request (url, data=None): + headers = { + 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)', + 'Accept': '*/*', + } + try: + req = Request(url, data, headers) + response = urlopen(req) + except IOError, e: + if hasattr(e, 'code'): + sys.stderr.write( '%s - ERROR %s' % (url, e.code) ) + raise; + return None + else: + return response + +def download(url, destination, max_size=MAX_SIZE): + response = browser_request(url, None) + rawimg = response.read() + if len(rawimg) == 0: + sys.stderr.write("got zero-length file") + raise; + if len(rawimg) > max_size: + sys.stderr.write("file too big: max size {} KB / {} is {} KB".format( + str(MAX_SIZE/1024), + destination, + str(len(rawimg)/1024) + )) + raise; + f = open(destination, "w") + f.write(rawimg) + f.close() + +def file_size (filepath): + try: + return os.stat(file)[6] + except Exception as e: + sys.stderr.write(str(e)) + raise; + +def gif_frames(filepath): + try: + info = Popen([BIN_IDENTIFY,filepath], stdout=PIPE).communicate()[0] + frames = filter((lambda x: x), map( + (lambda x: x.split(" ")[0]), + (info).split('\n') + )) + return frames + except Exception as e: + sys.stderr.write(str(e)) + raise; +#}}} + +class Imgrid(): + def __init__(self, params): + self.params = {} + self.tag = "imGrid" + self.now = now() + self.files_created = [] + self.commands = []; + self.required_keys = [ +#{{{ required_keys + "width", + "height", + "linethickness", + "opacity", + "linecolor", + "spacing", + "vlines", + "hlines", + "shadow", + "bgimage", + "bgcolor", + "imageinstead", + "planebgcolor", + "planebgimage", + "swing", + "tilt", + "roll", + "zoom", + "skycolor", + "transition", + "trim", + "format", + "username" +#}}} + ] + for k in self.required_keys: + if k in params: + if k in [ 'bgimage', 'planebgimage', 'imageinstead' ] and bool_correct(params[k]): + self.params[k] = {} + self.params[k]['url'] = params[k] + self.params[k]['filename'] = "IMGRIDTMP{}_{}".format(now(), k) + + self.params[k]['path'] = os.path.join(WORKING_DIR, self.params[k]['filename']) + try: + download(self.params[k]['url'], self.params[k]['path']) + self.files_created.append(self.params[k]['path']) + self.params[k]['mimetype'] = get_mimetype(self.params[k]['path']) + frames = gif_frames(self.params[k]['path']) + if len(frames) > 1: + self.params[k]['path'] = random.choice(frames) + + + except Exception as e: + sys.stderr.write(str(e)) + raise; + else: + self.params[k] = bool_correct(sanitize(params[k])) + else: + self.params[k] = False; + + self.params = dotdict(self.params) + + self.basename = self._get_filename(); + + if not self.params.finalformat: + self.params.finalformat = DEFAULT_FINALFORMAT + self.filename = "{}.{}".format(self.basename, self.params.finalformat) + #final filepath is stored in self.filepath + self.filepath = os.path.join(WORKING_DIR, self.filename) + + def _get_filename(self): + return "{}_{}_{}".format( + self.tag, + now(), + self.params.username or "" + ); + + #makes a canvas file...step 1 (if not bgimage) + def _make_canvas(self): + dimensions = "{}x{}".format( + self.params.width or DEFAULT_WIDTH, + self.params.height or DEFAULT_HEIGHT + ) + if self.params.bgimage: + return + bgcolor = "xc:{}".format(self.params.bgcolor or 'transparent') + cmd = [ BIN_CONVERT, "-size", dimensions, bgcolor, self.filepath ] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise; + + #2nd step-- run grid + def _grid_command(self): + cmd = [GRID] + if self.params.spacing: + if self.params.vlines: + width = 2 * int(self.params.width or DEFAULT_WIDTH) + cmd += ["-s","{},{}".format(self.params.spacing,width)] + elif self.params.hlines: + height = 2 * int(self.params.height or DEFAULT_HEIGHT) + cmd += ["-s", "{},{}".format(height,self.params.spacing)] + else: + cmd += ["-s",self.params.spacing] + cmd += [ "-c", self.params.linecolor or DEFAULT_LINE_COLOR] + if self.params.linethickness: cmd += ['-t',self.params.linethickness] + if self.params.opacity: cmd += ['-o',self.params.opacity] + cmd += [ self.filepath, self.filepath ] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise; + + def _shadow_cmd(self): + #convert 1.png \( +clone -background black -shadow 110x1+9+9 \) +swap -background none -layers merge +repage 2.png + cmd = [ + BIN_CONVERT, + self.filepath, + "(","+clone","-background","black","-shadow","100x2+20+10",")", + "+swap","-background","none","-layers","merge","+repage" , + self.filepath + ] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise; + + + def _threed_rotate_cmd (self): + #3rd step--run 3Drotate + cmd = [THREEDROTATE] + if self.params.swing: cmd += ["pan={}".format(self.params.swing)] + if self.params.tilt: cmd += ["tilt={}".format(self.params.tilt)] + if self.params.roll: cmd += ["roll={}".format(self.params.roll)] + if self.params.zoom: cmd += ["zoom={}".format(self.params.zoom)] + if cmd == [THREEDROTATE]: #if nothing has been added + return + if self.params.planebgcolor and not self.params.planebgimage: + cmd += ["bgcolor={}".format(self.params.planebgcolor)] + else: + cmd += ["bgcolor=none"] + cmd += ["skycolor={}".format(self.params.skycolor or 'none')] + if self.params.transition: cmd += ["vp={}",self.params.transition] + cmd += [ self.filepath, self.filepath ] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise + + def _trim_cmd (self): + if self.params.trim: + cmd = [BIN_CONVERT, self.filepath, "-trim", "+repage", self.filepath] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise + + def _prepare_gridimage(self, image): + if image['mimetype'] != 'png': + cmd = [BIN_CONVERT, image['path'], self.filepath] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise + else: + cmd = ['cp', image['path'], self.filepath] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise + + def _overlay_planebgimage(self): + cmd = [ + BIN_COMPOSITE, + "-compose", "Dst_Over", "-gravity", "center", + self.params.planebgimage["path"], + self.filepath, + self.filepath + ] + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise + + def _cleanup(self): + if not len(self.files_created): + pass + cmd = ["rm", "-f"] + self.files_created + try: + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception as e: + sys.stderr.write(str(e)) + raise + + def create(self): + if self.params.bgimage: + self._prepare_gridimage(self.params.bgimage) + self._grid_command() + elif self.params.imageinstead: + self._prepare_gridimage(self.params.imageinstead) + else: + self._make_canvas() + self._grid_command() + if self.params.shadow: self._shadow_cmd() + self._threed_rotate_cmd() + if self.params.planebgimage: self._overlay_planebgimage() + if self.params.trim: self._trim_cmd() + self._cleanup() + +if __name__ == "__main__": + g = Imgrid({ + 'bgimage' : 'http://i.asdf.us/im/1a/imBreak_1424909483_xx_abridged___.gif', + 'planebgimage' : 'http://i.imgur.com/FICZtph.png', + 'tilt' : '30', + 'spacing' : '30', + 'hlines' : 'true', + 'roll' : '30', + 'shadow' : 'true', + 'trim' : 'true' + }) + g.create() + print g.now + print g.filepath + print g.commands |
