summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPepper <pepper@scannerjammer.com>2015-02-28 01:29:03 -0500
committerPepper <pepper@scannerjammer.com>2015-02-28 01:29:03 -0500
commit3fe6738fe4b425a18e1505b53d7dc4b8045d39e4 (patch)
tree36cff89f6030570fae45fc5c912297428c71df01
parent9eb0b682a9321fbae87db5ef9ddc4f2253467bc0 (diff)
added impattern
-rwxr-xr-xpattern.py268
1 files changed, 268 insertions, 0 deletions
diff --git a/pattern.py b/pattern.py
new file mode 100755
index 0000000..b622aeb
--- /dev/null
+++ b/pattern.py
@@ -0,0 +1,268 @@
+#!/usr/bin/python2.7
+from subprocess import call, Popen, PIPE
+import urllib
+import urllib2
+import os
+import sys
+import random
+import re
+import time
+urlencode = urllib.urlencode
+urlopen = urllib2.urlopen
+Request = urllib2.Request
+
+import simplejson as json
+from PIL import Image
+import uuid
+
+WORKING_DIR = "/tmp"
+#WORKING_DIR = "/var/www/cache"
+BIN_COMPOSITE = "/usr/bin/composite"
+BIN_CONVERT = "/usr/bin/convert"
+BIN_IDENTIFY = "/usr/bin/identify"
+DEFAULT_FINALFORMAT = "png";
+
+MAX_SIZE = 1024 * 1024 * 1.2 * 1.5
+
+#FIXME make test params
+FUSE_MODE="Pin_Light"
+TEST_PARAMS = {
+# "pattern_url" : "http://asdf.us/impattern/patterns/1.png",
+ "pattern_data" : '{"matrix":[["0","0","0","0","0","1","0","0","0","0"],["0","0","0","0","1","1","1","0","0","0"],["0","0","1","1","1","0","1","0","0","0"],["0","1","1","0","0","0","0","0","0","0"],["0","1","0","0","1","0","0","0","0","0"],["0","1","0","0","1","0","0","0","1","0"],["0","1","0","0","1","1","0","0","1","0"],["0","1","0","0","0","1","1","1","1","0"],["0","1","1","1","1","0","0","0","0","0"],["0","0","0","0","1","0","0","0","0","0"]],"width":"10","height":"10"}',
+# "username" : "garfield",
+# "image_url" : "http://i.asdf.us/im/be/PinkHijab_1425078647_reye.gif",
+}
+
+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
+
+#{{{Utility functions
+class dotdict(dict):
+ """dot.notation access to dictionary attributes"""
+ def __getattr__(self, attr):
+ return self.get(attr)
+ __setattr__= dict.__setitem__
+ __delattr__= dict.__delitem__
+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 dimensions (filepath):
+ #works in lieu of a mimetype check (it reads the header as well)
+ ident = (Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]).split(" ")
+ return ident[2].split("x")
+
+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 Pattern:
+ def __init__(self, params):
+ self.params = {}
+ self.now = now()
+ self.tag = "imPattern";
+ self._pid = str(os.getpid())
+ self.commands = [];
+ self.height = ""
+ self.width = ""
+ self._required_keys = [
+ #FIXME change name to username in js
+ #FIXME change js api
+ "pattern_url",
+ "pattern_data",
+ "username",
+ "image_url",
+ ]
+ self.files_created = []
+ for k in self._required_keys:
+ if k in params:
+ if k in [ 'pattern_url', 'image_url' ]:
+ self.params[k] = params[k]
+ elif k == 'pattern_data':
+ self.params[k] = params[k] #FIXME add conversion data
+ else:
+ self.params[k] = sanitize(params[k])
+ else:
+ self.params[k] = False;
+
+ if not self.params['image_url']:
+ sys.stderr.write('no image url');
+ raise ValueError
+ self.params = dotdict(self.params)
+
+ self.basename, self._format = self._get_filename();
+ #FIXME omit file extension for downloaded files
+ self._downloaded_file = os.path.join(WORKING_DIR, "IMPATTERNTMP_DL{}_{}.{}".format(self.basename, self._pid, self._format)) # same here
+ #lets go back to this in a second
+ self._pattern_file = os.path.join(WORKING_DIR, "IMPATTERNTMP_PTN{}_{}.{}".format(self.basename, self._pid, self._format)) # this
+
+ self._download(self.params.image_url, self._downloaded_file)
+
+ self.width, self.height = dimensions(self._downloaded_file) # same here
+
+ self.filename = "{}.{}".format(self.basename, self._format)
+ self.filepath = os.path.join(WORKING_DIR, self.filename)
+
+ if self.params['pattern_url']:
+ self._download(self.params['pattern_url'], self._pattern_file)
+ elif self.params['pattern_data']:
+ self._from_pattern_data()
+ else:
+ sys.stderr.write("pattern must be supplied as json array or as a png url")
+ raise ValueError;
+
+ def _download(self, url, dest):
+ try:
+ download(url, dest)
+ self.files_created.append(dest)
+ except Exception as e:
+ sys.stderr.write(str(e))
+ raise;
+
+ def _call_cmd(self, cmd, error=""):
+ try:
+ call(cmd)
+ self.commands.append(" ".join(cmd));
+ except Exception as e:
+ if error:
+ sys.stderr.write(error)
+ else:
+ sys.stderr.write(str(e))
+
+ def _from_pattern_data(self):
+ def boolToColor(boolean):
+ if boolean:
+ return (0,0,0,255);
+ else:
+ return (255,255,255,255)
+ specs = json.loads(self.params.pattern_data);
+ if int(specs['width']) > 100 or int(specs['height']) > 100:
+ raise ValueError
+ sys.stderr.write("height and width need to be less than 100 px")
+ img = Image.new('RGBA', (int(specs['width']), int(specs['height'])));
+ pixels = img.load();
+ for i in range(0, len(specs['matrix'])):
+ for j in range(0, len(specs['matrix'][i])):
+ pixels[j,i] = boolToColor(int(specs['matrix'][i][j]));
+
+ img.save(self._pattern_file, "PNG")
+
+
+ def _get_filename (self):
+ url = self.params.image_url
+ name_part = "";
+ file_format = "";
+ if "?" in url:
+ url = url.split("?")[0]
+ if "/" in url:
+ url = urllib.unquote(url).replace(" ","")
+ name_part = url.split("/")[-1]
+ try:
+ parts = name_part.split(".")
+ name_part = sanitize(parts[-2])
+ file_format = sanitize(parts[-1])
+ if not name_part or not file_format:
+ sys.stderr.write( "Incompatible input file type")
+ raise;
+ except Exception as e:
+ sys.stderr.write( "Incompatible input file type")
+ raise;
+ else:
+ sys.stderr.write( "Incompatible url")
+ raise;
+ if (len(name_part) > 20):
+ name_part = name_part[:-20]
+ return "{}{}_{}_{}".format(self.tag, name_part, self.now, self.params.username or ""), file_format
+
+ def _cleanup(self):
+ cmd = ["rm"]+self.files_created
+ self._call_cmd(cmd)
+
+ #first step
+ def _make_canvas(self):
+ cmd = [BIN_CONVERT,"-size",self.width+"x"+self.height,"canvas:transparent", self.filepath]
+ self._call_cmd(cmd)
+
+ #second step use the Canvas as a background
+ def _make_mask(self):
+ #tile the pattern pattern on the canvas
+ cmd = [BIN_COMPOSITE,"-tile", self._pattern_file, self.filepath, self.filepath];
+ self._call_cmd(cmd)
+ #fuse the tiled file to create a mask
+ #convert thebg.gif -compose Dst_In null: thefile.gif -matte -layers composite new.gif
+ cmd = [BIN_CONVERT, self.filepath, "-compose", "Dst_In", "null:",
+ self._downloaded_file, "-matte", "-layers", "composite", self.filepath]
+ self._call_cmd(cmd)
+
+ #third step
+ def _fuse_mask(self, fuse_mode=FUSE_MODE):
+ cmd = [BIN_CONVERT, "-dispose", "2", self.filepath, "null:",
+ self._downloaded_file, "-matte", "-compose", fuse_mode, "-layers", "composite",
+ self.filepath]
+ self._call_cmd(cmd)
+
+ def create(self):
+ self._make_canvas();
+ self._make_mask()
+ self._fuse_mask();
+
+if __name__ == "__main__":
+ p = Pattern(TEST_PARAMS)
+ p.create()
+