Code sync
[external/hplip.git] / base / imagesize.py
1 # -*- coding: utf-8 -*-
2 #
3 # (c) Copyright 2001-2008 Hewlett-Packard 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
26 import os
27 import os.path
28 import re
29 import struct
30
31 # Re patterns
32 xbm_pat = re.compile(r'^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)', re.IGNORECASE)
33 xpm_pat = re.compile(r'"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*"', re.IGNORECASE)
34 ppm_pat1 = re.compile(r'^\#.*', re.IGNORECASE | re.MULTILINE)
35 ppm_pat2 = re.compile(r'^(P[1-6])\s+(\d+)\s+(\d+)', re.IGNORECASE)
36 ppm_pat3 = re.compile(r'IMGINFO:(\d+)x(\d+)', re.IGNORECASE)
37 tiff_endian_pat = re.compile(r'II\x2a\x00')
38
39
40 def readin(stream, length, offset=0):
41     if offset != 0:
42         stream.seek(offset, 0)
43
44     return stream.read(length)
45
46
47 def 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
60 def 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
72 def 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
81 def 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
105 def 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
129 def 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
163 def bmpsize(stream):
164     width, height = struct.unpack("<II", readin(stream, 8, 18))
165     return width, height
166
167
168 def 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
182 TYPE_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
195 def imagesize(filename, mime_type=''):
196     width, height = -1, -1
197
198     f = file(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