Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/PIL/ImImagePlugin.py: 22%

166 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1# 

2# The Python Imaging Library. 

3# $Id$ 

4# 

5# IFUNC IM file handling for PIL 

6# 

7# history: 

8# 1995-09-01 fl Created. 

9# 1997-01-03 fl Save palette images 

10# 1997-01-08 fl Added sequence support 

11# 1997-01-23 fl Added P and RGB save support 

12# 1997-05-31 fl Read floating point images 

13# 1997-06-22 fl Save floating point images 

14# 1997-08-27 fl Read and save 1-bit images 

15# 1998-06-25 fl Added support for RGB+LUT images 

16# 1998-07-02 fl Added support for YCC images 

17# 1998-07-15 fl Renamed offset attribute to avoid name clash 

18# 1998-12-29 fl Added I;16 support 

19# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) 

20# 2003-09-26 fl Added LA/PA support 

21# 

22# Copyright (c) 1997-2003 by Secret Labs AB. 

23# Copyright (c) 1995-2001 by Fredrik Lundh. 

24# 

25# See the README file for information on usage and redistribution. 

26# 

27 

28 

29import os 

30import re 

31 

32from . import Image, ImageFile, ImagePalette 

33 

34# -------------------------------------------------------------------- 

35# Standard tags 

36 

37COMMENT = "Comment" 

38DATE = "Date" 

39EQUIPMENT = "Digitalization equipment" 

40FRAMES = "File size (no of images)" 

41LUT = "Lut" 

42NAME = "Name" 

43SCALE = "Scale (x,y)" 

44SIZE = "Image size (x*y)" 

45MODE = "Image type" 

46 

47TAGS = { 

48 COMMENT: 0, 

49 DATE: 0, 

50 EQUIPMENT: 0, 

51 FRAMES: 0, 

52 LUT: 0, 

53 NAME: 0, 

54 SCALE: 0, 

55 SIZE: 0, 

56 MODE: 0, 

57} 

58 

59OPEN = { 

60 # ifunc93/p3cfunc formats 

61 "0 1 image": ("1", "1"), 

62 "L 1 image": ("1", "1"), 

63 "Greyscale image": ("L", "L"), 

64 "Grayscale image": ("L", "L"), 

65 "RGB image": ("RGB", "RGB;L"), 

66 "RLB image": ("RGB", "RLB"), 

67 "RYB image": ("RGB", "RLB"), 

68 "B1 image": ("1", "1"), 

69 "B2 image": ("P", "P;2"), 

70 "B4 image": ("P", "P;4"), 

71 "X 24 image": ("RGB", "RGB"), 

72 "L 32 S image": ("I", "I;32"), 

73 "L 32 F image": ("F", "F;32"), 

74 # old p3cfunc formats 

75 "RGB3 image": ("RGB", "RGB;T"), 

76 "RYB3 image": ("RGB", "RYB;T"), 

77 # extensions 

78 "LA image": ("LA", "LA;L"), 

79 "PA image": ("LA", "PA;L"), 

80 "RGBA image": ("RGBA", "RGBA;L"), 

81 "RGBX image": ("RGBX", "RGBX;L"), 

82 "CMYK image": ("CMYK", "CMYK;L"), 

83 "YCC image": ("YCbCr", "YCbCr;L"), 

84} 

85 

86# ifunc95 extensions 

87for i in ["8", "8S", "16", "16S", "32", "32F"]: 

88 OPEN[f"L {i} image"] = ("F", f"F;{i}") 

89 OPEN[f"L*{i} image"] = ("F", f"F;{i}") 

90for i in ["16", "16L", "16B"]: 

91 OPEN[f"L {i} image"] = (f"I;{i}", f"I;{i}") 

92 OPEN[f"L*{i} image"] = (f"I;{i}", f"I;{i}") 

93for i in ["32S"]: 

94 OPEN[f"L {i} image"] = ("I", f"I;{i}") 

95 OPEN[f"L*{i} image"] = ("I", f"I;{i}") 

96for i in range(2, 33): 

97 OPEN[f"L*{i} image"] = ("F", f"F;{i}") 

98 

99 

100# -------------------------------------------------------------------- 

101# Read IM directory 

102 

103split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") 

104 

105 

106def number(s): 

107 try: 

108 return int(s) 

109 except ValueError: 

110 return float(s) 

111 

112 

113## 

114# Image plugin for the IFUNC IM file format. 

115 

116 

117class ImImageFile(ImageFile.ImageFile): 

118 

119 format = "IM" 

120 format_description = "IFUNC Image Memory" 

121 _close_exclusive_fp_after_loading = False 

122 

123 def _open(self): 

124 

125 # Quick rejection: if there's not an LF among the first 

126 # 100 bytes, this is (probably) not a text header. 

127 

128 if b"\n" not in self.fp.read(100): 

129 raise SyntaxError("not an IM file") 

130 self.fp.seek(0) 

131 

132 n = 0 

133 

134 # Default values 

135 self.info[MODE] = "L" 

136 self.info[SIZE] = (512, 512) 

137 self.info[FRAMES] = 1 

138 

139 self.rawmode = "L" 

140 

141 while True: 

142 

143 s = self.fp.read(1) 

144 

145 # Some versions of IFUNC uses \n\r instead of \r\n... 

146 if s == b"\r": 

147 continue 

148 

149 if not s or s == b"\0" or s == b"\x1A": 

150 break 

151 

152 # FIXME: this may read whole file if not a text file 

153 s = s + self.fp.readline() 

154 

155 if len(s) > 100: 

156 raise SyntaxError("not an IM file") 

157 

158 if s[-2:] == b"\r\n": 

159 s = s[:-2] 

160 elif s[-1:] == b"\n": 

161 s = s[:-1] 

162 

163 try: 

164 m = split.match(s) 

165 except re.error as e: 

166 raise SyntaxError("not an IM file") from e 

167 

168 if m: 

169 

170 k, v = m.group(1, 2) 

171 

172 # Don't know if this is the correct encoding, 

173 # but a decent guess (I guess) 

174 k = k.decode("latin-1", "replace") 

175 v = v.decode("latin-1", "replace") 

176 

177 # Convert value as appropriate 

178 if k in [FRAMES, SCALE, SIZE]: 

179 v = v.replace("*", ",") 

180 v = tuple(map(number, v.split(","))) 

181 if len(v) == 1: 

182 v = v[0] 

183 elif k == MODE and v in OPEN: 

184 v, self.rawmode = OPEN[v] 

185 

186 # Add to dictionary. Note that COMMENT tags are 

187 # combined into a list of strings. 

188 if k == COMMENT: 

189 if k in self.info: 

190 self.info[k].append(v) 

191 else: 

192 self.info[k] = [v] 

193 else: 

194 self.info[k] = v 

195 

196 if k in TAGS: 

197 n += 1 

198 

199 else: 

200 

201 raise SyntaxError( 

202 "Syntax error in IM header: " + s.decode("ascii", "replace") 

203 ) 

204 

205 if not n: 

206 raise SyntaxError("Not an IM file") 

207 

208 # Basic attributes 

209 self._size = self.info[SIZE] 

210 self.mode = self.info[MODE] 

211 

212 # Skip forward to start of image data 

213 while s and s[:1] != b"\x1A": 

214 s = self.fp.read(1) 

215 if not s: 

216 raise SyntaxError("File truncated") 

217 

218 if LUT in self.info: 

219 # convert lookup table to palette or lut attribute 

220 palette = self.fp.read(768) 

221 greyscale = 1 # greyscale palette 

222 linear = 1 # linear greyscale palette 

223 for i in range(256): 

224 if palette[i] == palette[i + 256] == palette[i + 512]: 

225 if palette[i] != i: 

226 linear = 0 

227 else: 

228 greyscale = 0 

229 if self.mode in ["L", "LA", "P", "PA"]: 

230 if greyscale: 

231 if not linear: 

232 self.lut = list(palette[:256]) 

233 else: 

234 if self.mode in ["L", "P"]: 

235 self.mode = self.rawmode = "P" 

236 elif self.mode in ["LA", "PA"]: 

237 self.mode = "PA" 

238 self.rawmode = "PA;L" 

239 self.palette = ImagePalette.raw("RGB;L", palette) 

240 elif self.mode == "RGB": 

241 if not greyscale or not linear: 

242 self.lut = list(palette) 

243 

244 self.frame = 0 

245 

246 self.__offset = offs = self.fp.tell() 

247 

248 self._fp = self.fp # FIXME: hack 

249 

250 if self.rawmode[:2] == "F;": 

251 

252 # ifunc95 formats 

253 try: 

254 # use bit decoder (if necessary) 

255 bits = int(self.rawmode[2:]) 

256 if bits not in [8, 16, 32]: 

257 self.tile = [("bit", (0, 0) + self.size, offs, (bits, 8, 3, 0, -1))] 

258 return 

259 except ValueError: 

260 pass 

261 

262 if self.rawmode in ["RGB;T", "RYB;T"]: 

263 # Old LabEye/3PC files. Would be very surprised if anyone 

264 # ever stumbled upon such a file ;-) 

265 size = self.size[0] * self.size[1] 

266 self.tile = [ 

267 ("raw", (0, 0) + self.size, offs, ("G", 0, -1)), 

268 ("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), 

269 ("raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1)), 

270 ] 

271 else: 

272 # LabEye/IFUNC files 

273 self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] 

274 

275 @property 

276 def n_frames(self): 

277 return self.info[FRAMES] 

278 

279 @property 

280 def is_animated(self): 

281 return self.info[FRAMES] > 1 

282 

283 def seek(self, frame): 

284 if not self._seek_check(frame): 

285 return 

286 

287 self.frame = frame 

288 

289 if self.mode == "1": 

290 bits = 1 

291 else: 

292 bits = 8 * len(self.mode) 

293 

294 size = ((self.size[0] * bits + 7) // 8) * self.size[1] 

295 offs = self.__offset + frame * size 

296 

297 self.fp = self._fp 

298 

299 self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] 

300 

301 def tell(self): 

302 return self.frame 

303 

304 

305# 

306# -------------------------------------------------------------------- 

307# Save IM files 

308 

309 

310SAVE = { 

311 # mode: (im type, raw mode) 

312 "1": ("0 1", "1"), 

313 "L": ("Greyscale", "L"), 

314 "LA": ("LA", "LA;L"), 

315 "P": ("Greyscale", "P"), 

316 "PA": ("LA", "PA;L"), 

317 "I": ("L 32S", "I;32S"), 

318 "I;16": ("L 16", "I;16"), 

319 "I;16L": ("L 16L", "I;16L"), 

320 "I;16B": ("L 16B", "I;16B"), 

321 "F": ("L 32F", "F;32F"), 

322 "RGB": ("RGB", "RGB;L"), 

323 "RGBA": ("RGBA", "RGBA;L"), 

324 "RGBX": ("RGBX", "RGBX;L"), 

325 "CMYK": ("CMYK", "CMYK;L"), 

326 "YCbCr": ("YCC", "YCbCr;L"), 

327} 

328 

329 

330def _save(im, fp, filename): 

331 

332 try: 

333 image_type, rawmode = SAVE[im.mode] 

334 except KeyError as e: 

335 raise ValueError(f"Cannot save {im.mode} images as IM") from e 

336 

337 frames = im.encoderinfo.get("frames", 1) 

338 

339 fp.write(f"Image type: {image_type} image\r\n".encode("ascii")) 

340 if filename: 

341 # Each line must be 100 characters or less, 

342 # or: SyntaxError("not an IM file") 

343 # 8 characters are used for "Name: " and "\r\n" 

344 # Keep just the filename, ditch the potentially overlong path 

345 name, ext = os.path.splitext(os.path.basename(filename)) 

346 name = "".join([name[: 92 - len(ext)], ext]) 

347 

348 fp.write(f"Name: {name}\r\n".encode("ascii")) 

349 fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii")) 

350 fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) 

351 if im.mode in ["P", "PA"]: 

352 fp.write(b"Lut: 1\r\n") 

353 fp.write(b"\000" * (511 - fp.tell()) + b"\032") 

354 if im.mode in ["P", "PA"]: 

355 fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes 

356 ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))]) 

357 

358 

359# 

360# -------------------------------------------------------------------- 

361# Registry 

362 

363 

364Image.register_open(ImImageFile.format, ImImageFile) 

365Image.register_save(ImImageFile.format, _save) 

366 

367Image.register_extension(ImImageFile.format, ".im")