1# -*- coding: utf-8 -*- 2# 3# (c) Copyright 2001-2015 HP Development Company, L.P. 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 2 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, write to the Free Software 17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18# 19# Author: Don Welch 20 21# 22# Ported from Perl's Image::Size module by Randy J. Ray 23# 24 25# Std Lib 26import os 27import os.path 28import re 29import struct 30 31# Re patterns 32xbm_pat = re.compile(r'^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)', re.IGNORECASE) 33xpm_pat = re.compile(r'"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*"', re.IGNORECASE) 34ppm_pat1 = re.compile(r'^\#.*', re.IGNORECASE | re.MULTILINE) 35ppm_pat2 = re.compile(r'^(P[1-6])\s+(\d+)\s+(\d+)', re.IGNORECASE) 36ppm_pat3 = re.compile(r'IMGINFO:(\d+)x(\d+)', re.IGNORECASE) 37tiff_endian_pat = re.compile(r'II\x2a\x00') 38 39 40def readin(stream, length, offset=0): 41 if offset != 0: 42 stream.seek(offset, 0) 43 44 return stream.read(length) 45 46 47def xbmsize(stream): 48 width, height = -1, -1 49 match = xbm_pat.match(readin(stream,1024)) 50 51 try: 52 width = int(match.group(1)) 53 height = int(match.group(2)) 54 except: 55 pass 56 57 return width, height 58 59 60def xpmsize(stream): 61 width, height = -1, -1 62 match = re.search(xpm_pat, readin(stream, 1024)) 63 try: 64 width = int(match.group(1)) 65 height = int(match.group(2)) 66 except: 67 pass 68 69 return width, height 70 71 72def pngsize(stream): # also does MNG 73 width, height = -1, -1 74 75 if readin(stream, 4, 12) in ('IHDR', 'MHDR'): 76 height, width = struct.unpack("!II", stream.read(8)) 77 78 return width,height 79 80 81def jpegsize(stream): 82 width, height = -1, -1 83 stream.seek(2) 84 while True: 85 length = 4 86 buffer = readin(stream, length) 87 try: 88 marker, code, length = struct.unpack("!c c h", buffer) 89 except: 90 break 91 92 if marker != '\xff': 93 break 94 95 if 0xc0 <= ord(code) <= 0xc3: 96 length = 5 97 height, width = struct.unpack("!xhh", readin(stream, length)) 98 99 else: 100 readin(stream, length-2) 101 102 return width, height 103 104 105def ppmsize(stream): 106 width, height = -1, -1 107 header = re.sub(ppm_pat1, '', readin(stream, 1024)) 108 match = ppm_pat2.match(header) 109 typ = '' 110 try: 111 typ = match.group(1) 112 width = int(match.group(2)) 113 height = int(match.group(3)) 114 except: 115 pass 116 117 if typ == 'P7': 118 match = ppm_pat3.match(header) 119 120 try: 121 width = int(match.group(1)) 122 height = int(match.group(2)) 123 except: 124 pass 125 126 return width, height 127 128 129def tiffsize(stream): 130 header = readin(stream, 4) 131 endian = ">" 132 match = tiff_endian_pat.match(header) 133 134 if match is not None: 135 endian = "<" 136 137 input = readin(stream, 4, 4) 138 offset = struct.unpack('%si' % endian, input)[0] 139 num_dirent = struct.unpack('%sH' % endian, readin(stream, 2, offset))[0] 140 offset += 2 141 num_dirent = offset+(num_dirent*12) 142 width, height = -1, -1 143 144 while True: 145 ifd = readin(stream, 12, offset) 146 147 if ifd == '' or offset > num_dirent: 148 break 149 150 offset += 12 151 tag = struct.unpack('%sH'% endian, ifd[0:2])[0] 152 type = struct.unpack('%sH' % endian, ifd[2:4])[0] 153 154 if tag == 0x0100: 155 width = struct.unpack("%si" % endian, ifd[8:12])[0] 156 157 elif tag == 0x0101: 158 height = struct.unpack("%si" % endian, ifd[8:12])[0] 159 160 return width, height 161 162 163def bmpsize(stream): 164 width, height = struct.unpack("<II", readin(stream, 8, 18)) 165 return width, height 166 167 168def gifsize(stream): 169 # since we only care about the printed size of the image 170 # we only need to get the logical screen sizes, which are 171 # the maximum extents of the image. This code is much simpler 172 # than the code from Image::Size 173 #width, height = -1, -1 174 buf = readin(stream, 7, 6) # LSx, GCTF, etc 175 height, width, flags, bci, par = struct.unpack('<HHBBB', buf) 176 177 return width, height 178 179 180 181 182TYPE_MAP = {re.compile('^GIF8[7,9]a') : ('image/gif', gifsize), 183 re.compile("^\xFF\xD8") : ('image/jpeg', jpegsize), 184 re.compile("^\x89PNG\x0d\x0a\x1a\x0a") : ('image/png', pngsize), 185 re.compile("^P[1-7]") : ('image/x-portable-pixmap', ppmsize), 186 re.compile('\#define\s+\S+\s+\d+') : ('image/x-xbitmap', xbmsize), 187 re.compile('\/\* XPM \*\/') : ('image/x-xpixmap', xpmsize), 188 re.compile('^MM\x00\x2a') : ('image/tiff', tiffsize), 189 re.compile('^II\*\x00') : ('image/tiff', tiffsize), 190 re.compile('^BM') : ('image/x-bitmap', bmpsize), 191 re.compile("^\x8aMNG\x0d\x0a\x1a\x0a") : ('image/png', pngsize), 192 } 193 194 195def imagesize(filename, mime_type=''): 196 width, height = -1, -1 197 198 f = open(filename, 'r') 199 buffer = f.read(4096) 200 201 if not mime_type: 202 for t in TYPE_MAP: 203 match = t.search(buffer) 204 if match is not None: 205 mime_type, func = TYPE_MAP[t] 206 break 207 208 if mime_type and func: 209 f.seek(0) 210 width, height = func(f) 211 else: 212 width, height = -1, -1 213 214 f.close() 215 216 return height, width, mime_type 217 218