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