1 """Recognize image file formats based on their first few bytes.""" 2 3 from os import PathLike 4 5 __all__ = ["what"] 6 7 #-------------------------# 8 # Recognize image headers # 9 #-------------------------# 10 11 def what(file, h=None): 12 f = None 13 try: 14 if h is None: 15 if isinstance(file, (str, PathLike)): 16 f = open(file, 'rb') 17 h = f.read(32) 18 else: 19 location = file.tell() 20 h = file.read(32) 21 file.seek(location) 22 for tf in tests: 23 res = tf(h, f) 24 if res: 25 return res 26 finally: 27 if f: f.close() 28 return None 29 30 31 #---------------------------------# 32 # Subroutines per image file type # 33 #---------------------------------# 34 35 tests = [] 36 37 def test_jpeg(h, f): 38 """JPEG data in JFIF or Exif format""" 39 if h[6:10] in (b'JFIF', b'Exif'): 40 return 'jpeg' 41 42 tests.append(test_jpeg) 43 44 def test_png(h, f): 45 if h.startswith(b'\211PNG\r\n\032\n'): 46 return 'png' 47 48 tests.append(test_png) 49 50 def test_gif(h, f): 51 """GIF ('87 and '89 variants)""" 52 if h[:6] in (b'GIF87a', b'GIF89a'): 53 return 'gif' 54 55 tests.append(test_gif) 56 57 def test_tiff(h, f): 58 """TIFF (can be in Motorola or Intel byte order)""" 59 if h[:2] in (b'MM', b'II'): 60 return 'tiff' 61 62 tests.append(test_tiff) 63 64 def test_rgb(h, f): 65 """SGI image library""" 66 if h.startswith(b'\001\332'): 67 return 'rgb' 68 69 tests.append(test_rgb) 70 71 def test_pbm(h, f): 72 """PBM (portable bitmap)""" 73 if len(h) >= 3 and \ 74 h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': 75 return 'pbm' 76 77 tests.append(test_pbm) 78 79 def test_pgm(h, f): 80 """PGM (portable graymap)""" 81 if len(h) >= 3 and \ 82 h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': 83 return 'pgm' 84 85 tests.append(test_pgm) 86 87 def test_ppm(h, f): 88 """PPM (portable pixmap)""" 89 if len(h) >= 3 and \ 90 h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': 91 return 'ppm' 92 93 tests.append(test_ppm) 94 95 def test_rast(h, f): 96 """Sun raster file""" 97 if h.startswith(b'\x59\xA6\x6A\x95'): 98 return 'rast' 99 100 tests.append(test_rast) 101 102 def test_xbm(h, f): 103 """X bitmap (X10 or X11)""" 104 if h.startswith(b'#define '): 105 return 'xbm' 106 107 tests.append(test_xbm) 108 109 def test_bmp(h, f): 110 if h.startswith(b'BM'): 111 return 'bmp' 112 113 tests.append(test_bmp) 114 115 def test_webp(h, f): 116 if h.startswith(b'RIFF') and h[8:12] == b'WEBP': 117 return 'webp' 118 119 tests.append(test_webp) 120 121 def test_exr(h, f): 122 if h.startswith(b'\x76\x2f\x31\x01'): 123 return 'exr' 124 125 tests.append(test_exr) 126 127 #--------------------# 128 # Small test program # 129 #--------------------# 130 131 def test(): 132 import sys 133 recursive = 0 134 if sys.argv[1:] and sys.argv[1] == '-r': 135 del sys.argv[1:2] 136 recursive = 1 137 try: 138 if sys.argv[1:]: 139 testall(sys.argv[1:], recursive, 1) 140 else: 141 testall(['.'], recursive, 1) 142 except KeyboardInterrupt: 143 sys.stderr.write('\n[Interrupted]\n') 144 sys.exit(1) 145 146 def testall(list, recursive, toplevel): 147 import sys 148 import os 149 for filename in list: 150 if os.path.isdir(filename): 151 print(filename + '/:', end=' ') 152 if recursive or toplevel: 153 print('recursing down:') 154 import glob 155 names = glob.glob(os.path.join(filename, '*')) 156 testall(names, recursive, 0) 157 else: 158 print('*** directory (use -r) ***') 159 else: 160 print(filename + ':', end=' ') 161 sys.stdout.flush() 162 try: 163 print(what(filename)) 164 except OSError: 165 print('*** not found ***') 166 167 if __name__ == '__main__': 168 test() 169