Commits

Mike Orr committed fa9b020

Reference implementation for getting the dimensions of an image.

  • Participants
  • Parent commits 2d2f458

Comments (0)

Files changed (1)

File unfinished/image_size.py

+"""Calculate the size of an image.
+
+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.
+"""
+
+#!/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')