Commits

Mike Orr committed c6e5e56

Add webhelpers.media with helpers for calculating image sizes.

Comments (0)

Files changed (3)

 
 0.6.2 (tip)
 * nl2br() and format-paragraphs were not literal-safe.
+* New module webhelpers.media for multimedia helpers. Currently contains
+  functions to determine the size of images and to choose a scaling factor.
 
 0.6.1 (7/31/2008)
 * Include a faster version of cgi.escape for use by the literal object.

unfinished/image_size.py

-"""Image helpers
-
-The following module extracts the width and height from an image file with
-No C code or external dependencies.
-
-This version is too complicated and GPL, but serves as an algorithm 
-reference.  It was downloaded from
-http://www.pycode.com/modules/?id=32&tab=download
-
-We need a get_dimensions() function for JPG/PNG/GIF.
-Also, I hesitate to return -1 for an unknown dimension because what would
-a web browser do with it?  Should either return None or raise an exception.
-I've never seen an image without valid dimensions in it, so raising an
-exception may be fine.
-
-WebHelpers/unfinished/multimedia.py contains an alternative
-``get_dimensions()`` function that depends on the Python Imaging Library.
-"""
-
-#!/usr/bin/env python
-# (c) Copyright 2001-2005 Hewlett-Packard Development Company, L.P.
-#
-# 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
-#
-# Author: Don Welch
-# Ported from Perl's Image::Size module by Randy J. Ray
-# Modified by Perenzo, 2006
-
-import os, os.path, re, struct
-
-xbm_pat = re.compile(r'^#defines*S*s*(d+)s*n#defines*S*s*(d+)', re.IGNORECASE)
-xpm_pat = re.compile(r'"s*(d+)s+(d+)(s+d+s+d+){1,2}s*"', re.IGNORECASE)
-ppm_pat1 = re.compile(r'^#.*', re.IGNORECASE | re.MULTILINE)
-ppm_pat2 = re.compile(r'^(P[1-6])s+(d+)s+(d+)', re.IGNORECASE)
-ppm_pat3 = re.compile(r'IMGINFO:(d+)x(d+)', re.IGNORECASE)
-tiff_endian_pat = re.compile(r'IIx2ax00')
-
-def readin(stream, length, offset=0):
-   if offset != 0:
-       stream.seek(offset, 0)
-   return stream.read(length)
-
-def xbmsize(stream):
-   width, height = -1, -1
-   match = xbm_pat.match(readin(stream, 1024))
-   try:
-       width = int(match.group(1))
-       height = int(match.group(2))
-   except:
-       pass
-   return width, height
-
-def xpmsize(stream):
-   width, height = -1, -1
-   match = re.search(xpm_pat, readin(stream, 1024))
-   try:
-       width = int(match.group(1))
-       height = int(match.group(2))
-   except:
-       pass
-   return width, height
-
-def pngsize(stream): # also does MNG
-   width, height = -1, -1
-   if readin(stream, 4, 12) in ('IHDR', 'MHDR'):
-       height, width = struct.unpack("!II", stream.read(8))
-
-   return width, height
-
-def jpegsize(stream):
-   width, height = -1, -1
-   stream.seek(2)
-   while 1:
-       length = 4
-       buffer = readin(stream, length)
-       try:
-           marker, code, length = struct.unpack("!ccH", buffer)
-       except:
-           break
-       if marker != 'xff':
-           break
-       if 0xc0 <= ord(code) <= 0xc3:
-           length = 5
-           height, width = struct.unpack("!xHH", readin(stream, length))
-       else:
-           readin(stream, length-2)
-   return width, height
-
-def ppmsize(stream):
-   width, height = -1, -1
-   header = re.sub(ppm_pat1, '', readin(stream, 1024))
-   match = ppm_pat2.match(header)
-   typ = ''
-   try:
-       typ = match.group(1)
-       width = int(match.group(2))
-       height = int(match.group(3))
-   except:
-       pass
-   if typ == 'P7':
-       match = ppm_pat3.match(header)
-
-       try:
-           width = int(match.group(1))
-           height = int(match.group(2))
-       except:
-           pass
-   return width, height
-
-def tiffsize(stream):
-   header = readin(stream, 4)
-   endian = ">"
-   match = tiff_endian_pat.match(header)
-   if match is not None:
-       endian = "<"
-   input = readin(stream, 4, 4)
-   offset = struct.unpack('%si' % endian, input)[0]
-   num_dirent = struct.unpack('%sH' % endian, readin(stream, 2, offset))[0]
-   offset += 2
-   num_dirent = offset+(num_dirent*12)
-   width, height = -1, -1
-   while True:
-       ifd = readin(stream, 12, offset)
-       if ifd == '' or offset > num_dirent:
-           break
-       offset += 12
-       tag = struct.unpack('%sH'% endian, ifd[0:2])[0]
-       type = struct.unpack('%sH' % endian, ifd[2:4])[0]
-       if tag == 0x0100:
-           width = struct.unpack("%si" % endian, ifd[8:12])[0]
-       elif tag == 0x0101:
-           height = struct.unpack("%si" % endian, ifd[8:12])[0]
-   return width, height
-
-def bmpsize(stream):
-   width, height = struct.unpack("<II", readin(stream, 8, 18))
-   return width, height
-
-def gifsize(stream):
-   # since we only care about the printed size of the image
-   # we only need to get the logical screen sizes, which are
-   # the maximum extents of the image. This code is much simpler
-   # than the code from Image::Size
-   #width, height = -1, -1
-   buf = readin(stream, 7, 6) # LSx, GCTF, etc
-   height, width, flags, bci, par = struct.unpack('<HHBBB', buf)
-   return width, height
-
-TYPE_MAP = { re.compile('^GIF8[7,9]a')              : ('image/gif', gifsize),
-            re.compile("^xFFxD8")                : ('image/jpeg', jpegsize),
-            re.compile("^x89PNGx0dx0ax1ax0a") : ('image/png', pngsize),
-            re.compile("^P[1-7]")                  : ('image/x-portable-pixmap', ppmsize),
-            re.compile('#defines+S+s+d+')     : ('image/x-xbitmap', xbmsize),
-            re.compile('/* XPM */')            : ('image/x-xpixmap', xpmsize),
-            re.compile('^MMx00x2a')              : ('image/tiff', tiffsize),
-            re.compile('^II*x00')                : ('image/tiff', tiffsize),
-            re.compile('^BM')                      : ('image/x-bitmap', bmpsize),
-            re.compile("^x8aMNGx0dx0ax1ax0a") : ('image/png', pngsize),
-          }
-
-def imagesize(filename, mime_type=''):
-   width, height = -1, -1
-   f = file(filename, 'rb')
-   buffer = f.read(4096)
-   if not mime_type:
-       for t in TYPE_MAP:
-           match = t.search(buffer)
-           if match is not None:
-               mime_type, func = TYPE_MAP[t]
-               break
-   if mime_type and func:
-       f.seek(0)
-       width, height = func(f)
-   else:
-       width, height = -1, -1
-   f.close()
-   return height, width, mime_type
-
-if __name__=="__main__":
-   print imagesize('f:\test.jpg')

webhelpers/media.py

+"""Multimedia helpers for images, etc."""
+
+import logging
+import os
+import struct
+import sys
+
+__all__ = ["choose_height", "get_dimensions_pil", "get_dimensions"]
+
+def choose_height(new_width, width, height):
+    """Return the height corresponding to ``new_width`` that's proportional
+       to the original size (``width`` x ``height``).
+    """
+    proportion = float(height) / float(width)
+    return int(new_width * proportion)
+
+def get_dimensions_pil(path, default=(None, None)):
+    """Get an image's size using the Python Imaging Library (PIL)
+
+    Returns ``(width, height)`` as two integers, or ``default`` if the size
+    could not be ascertained.  Failuer usually means the file does not exist
+    or is not in a format recognized by PIL.
+
+    Depends on the Python Imaging Library (http://pypi.python.org/pypi/PIL).
+    See ``get_dimensions()`` if your application is not otherwise using PIL.
+    """
+    import Image
+    try:
+        im = Image(path)
+    except Exception:
+        return default
+    return im.size
+
+def get_dimensions(path, default=(None, None)):
+    """Get an image's size using only the Python standard library
+
+    Returns ``(width, height)`` as two integers, or ``default`` if the size
+    could not be ascertained.  Failure usually means the file does not exist
+    or is not in a recognized format.  Only JPG/PNG/GIF/BMP are supported at
+    this time.
+
+    The algorithms are based on a PyCode recipe by Perenzo/Welch/Ray.
+    (http://www.pycode.com/modules/?id=32&tab=download)
+
+    This helper recognizes fewer image formats and is potentially less
+    accurate than ``get_dimensions_pil()``.
+    """
+    apath = os.path.abspath(path)
+    try:
+        f = open(path, "rb")
+    except IOError:
+        return default
+    try:
+        header = f.read(1024)
+        # JPG
+        if header.startswith("\xFF\xD8"):
+            width = height = None
+            f.seek(2)
+            while True:
+                length = 4
+                buf = f.read(length)
+                try:
+                    marker, code, length = struct.unpack("!ccH", buf)
+                except Exception:
+                    break
+                if marker != "\xff":
+                    break
+                if 0xc0 <= ord(code) <= 0xc3:
+                    length = 5
+                    buf = f.read(length)
+                    height, width = struct.unpack("!xHH", buf)
+                else:
+                    f.read(length-2)
+            return width, height
+        # PNG
+        elif header.startswith("\x89PNG\x0d\x0a\x1a\x0a") or \
+            header.startswith("\x8aMNG\x0d\x0a\x1a\x0a"):
+            f.seek(12)
+            control = f.read(4)
+            if control in ["IHDR", "MHDR"]:
+                buf = f.read(8)
+                width, height = struct.unpack("!II", buf)
+                return width, height
+        # GIF
+        elif header.startswith("GIF87a") or header.startswith("GIF89a"):
+            f.seek(6)
+            buf = f.read(7)
+            width, height, flags, bci, par = struct.unpack("<HHBBB", buf)
+            return width, height
+        # BMP
+        elif header.startswith("BM"):
+            f.seek(18)
+            buf = f.read(8)
+            width, height = struct.unpack("<II", buf)
+            return width, height
+        # Unknown
+        return default
+    finally:
+        f.close()
+
+def test_get_dimensions():
+    files = sys.argv[1:]
+    if not files:
+        sys.exit("usage: %s FILES ...\nPrints dimensions of each image")
+    for file in files:
+        apath = os.path.abspath(file)
+        print "%s:" % apath,
+        if not os.path.isfile(file):
+            print "does not exist or is not a plain file"
+            continue
+        width, height = get_dimensions(file)
+        if width is None and height is None:
+            print "could not get dimensions"
+        else:
+            if width is None:
+                width = "UNKNOWN"
+            if height is None:
+                height = "UNKNOWN"
+            print "%s x %s" % (width, height)
+            
+        
+
+if __name__ == "__main__":  test_get_dimensions()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.