summaryrefslogtreecommitdiff
path: root/imgrid.py
diff options
context:
space:
mode:
authorPepper <pepper@scannerjammer.com>2015-02-26 02:03:24 -0500
committerPepper <pepper@scannerjammer.com>2015-02-26 02:03:24 -0500
commitd489eb062653a2726d4fbd42bfd34835dc770d4b (patch)
treeb6f73ee095d713feb7b656e9d178126e83a62f63 /imgrid.py
first
Diffstat (limited to 'imgrid.py')
-rwxr-xr-ximgrid.py356
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