def SOF(self, marker): # # Start of frame marker. Defines the size and mode of the # image. JPEG is color blind, so we use some simple # heuristics to map the number of layers to an appropriate # mode. Note that this could be made a bit brighter, by # looking for JFIF and Adobe APP markers. n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) self.size = i16(s[3:]), i16(s[1:]) self.bits = ord(s[0]) if self.bits != 8: raise SyntaxError("cannot handle %d-bit layers" % self.bits) self.layers = ord(s[5]) if self.layers == 1: self.mode = "L" elif self.layers == 3: self.mode = "RGB" elif self.layers == 4: self.mode = "CMYK" else: raise SyntaxError("cannot handle %d-layer images" % self.layers) if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: self.info["progression"] = 1 for i in range(6, len(s), 3): t = s[i:i+3] # 4-tuples: id, vsamp, hsamp, qtable self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2])))
def chunk_iTXt(self, pos, length): # international text r = s = ImageFile._safe_read(self.fp, length) try: k, r = r.split(b"\0", 1) except ValueError: return s if len(r) < 2: return s cf, cm, r = i8(r[0]), i8(r[1]), r[2:] try: lang, tk, v = r.split(b"\0", 2) except ValueError: return s if cf != 0: if cm == 0: try: v = zlib.decompress(v) except zlib.error: return s else: return s if bytes is not str: try: k = k.decode("latin-1", "strict") lang = lang.decode("utf-8", "strict") tk = tk.decode("utf-8", "strict") v = v.decode("utf-8", "strict") except UnicodeError: return s self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) return s
def chunk_zTXt(self, pos, len): # compressed text s = ImageFile._safe_read(self.fp, len) try: k, v = s.split(b"\0", 1) except ValueError: k = s; v = b"" if v: comp_method = i8(v[0]) else: comp_method = 0 if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib try: v = zlib.decompress(v[1:]) except zlib.error: v = b"" if k: if bytes is not str: k = k.decode('latin-1', 'strict') v = v.decode('latin-1', 'replace') self.im_info[k] = self.im_text[k] = v return s
def chunk_iCCP(self, pos, length): # ICC profile s = ImageFile._safe_read(self.fp, length) # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) i = s.find(b"\0") logger.debug("iCCP profile name %r", s[:i]) logger.debug("Compression method %s", i8(s[i])) comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: icc_profile = _safe_zlib_decompress(s[i+2:]) except ValueError: if ImageFile.LOAD_TRUNCATED_IMAGES: icc_profile = None else: raise except zlib.error: icc_profile = None # FIXME self.im_info["icc_profile"] = icc_profile return s
def chunk_zTXt(self, pos, length): # compressed text s = ImageFile._safe_read(self.fp, length) try: k, v = s.split(b"\0", 1) except ValueError: k = s v = b"" if v: comp_method = i8(v[0]) else: comp_method = 0 if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) try: v = _safe_zlib_decompress(v[1:]) except zlib.error: v = b"" if k: if bytes is not str: k = k.decode("latin-1", "strict") v = v.decode("latin-1", "replace") self.im_info[k] = self.im_text[k] = v self.check_text_memory(len(v)) return s
def chunk_PLTE(self, pos, length): # palette s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": self.im_palette = "RGB", s return s
def load(self, fp): # load tag dictionary self.reset() self.offset = fp.tell() i16 = self.i16 i32 = self.i32 for i in range(i16(fp.read(2))): ifd = fp.read(12) tag, typ = i16(ifd), i16(ifd, 2) if Image.DEBUG: from PIL import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") print("tag: %s (%d)" % (tagname, tag), end=' ') print("- type: %s (%d)" % (typname, typ), end=' ') try: dispatch = self.load_dispatch[typ] except KeyError: if Image.DEBUG: print("- unsupported type", typ) continue # ignore unsupported type size, handler = dispatch size = size * i32(ifd, 4) # Get and expand tag value if size > 4: here = fp.tell() fp.seek(i32(ifd, 8)) data = ImageFile._safe_read(fp, size) fp.seek(here) else: data = ifd[8:8+size] if len(data) != size: warnings.warn("Possibly corrupt EXIF data. " "Expecting to read %d bytes but only got %d. " "Skipping tag %s" % (size, len(data), tag)) continue self.tagdata[tag] = data self.tagtype[tag] = typ if Image.DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): print("- value: <table: %d bytes>" % size) else: print("- value:", self[tag]) self.next = i32(fp.read(4))
def COM(self, marker): # # Comment marker. Store these in the APP dictionary. n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) self.app["COM"] = s # compatibility self.applist.append(("COM", s))
def load(self, fp): self.reset() self._offset = fp.tell() try: for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]): tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12)) if DEBUG: tagname = TAGS_V2.get(tag, TagInfo()).name typname = TYPES.get(typ, "unknown") print( "tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), end=" ") try: unit_size, handler = self._load_dispatch[typ] except KeyError: if DEBUG: print("- unsupported type", typ) continue # ignore unsupported type size = count * unit_size if size > 4: here = fp.tell() offset, = self._unpack("L", data) if DEBUG: print( "Tag Location: %s - Data Location: %s" % (here, offset), end=" ") fp.seek(offset) data = ImageFile._safe_read(fp, size) fp.seek(here) else: data = data[:size] if len(data) != size: warnings.warn( "Possibly corrupt EXIF data. " "Expecting to read %d bytes but only got %d. " "Skipping tag %s" % (size, len(data), tag)) continue self._tagdata[tag] = data self.tagtype[tag] = typ if DEBUG: if size > 32: print("- value: <table: %d bytes>" % size) else: print("- value:", self[tag]) self.next, = self._unpack("L", self._ensure_read(fp, 4)) except IOError as msg: warnings.warn(str(msg)) return
def APP(self, marker): # # Application marker. Store these in the APP dictionary. # Also look for well-known application markers. n = i16(self.fp.read(2)) - 2 s = ImageFile._safe_read(self.fp, n) app = "APP%d" % (marker & 15) self.app[app] = s # compatibility self.applist.append((app, s)) if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: jfif_unit = i8(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass else: if jfif_unit == 1: self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": # Since an ICC profile can be larger than the maximum size of # a JPEG marker (64K), we need provisions to split it into # multiple markers. The format defined by the ICC specifies # one or more APP2 markers containing the following data: # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) # Marker sequence number 1, 2, etc (1 byte) # Number of markers Total of APP2's used (1 byte) # Profile data (remainder of APP2 data) # Decoders should use the marker sequence numbers to # reassemble the profile, rather than assuming that the APP2 # markers appear in the correct sequence. self.icclist.append(s) elif marker == 0xFFEE and s[:5] == b"Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: adobe_transform = i8(s[1]) except: pass else: self.info["adobe_transform"] = adobe_transform
def patched_chunk_tRNS(self, pos, len): i16 = PngImagePlugin.i16 s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": self.im_info["transparency"] = map(ord, s) elif self.im_mode == "L": self.im_info["transparency"] = i16(s) elif self.im_mode == "RGB": self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s
def load(self, fp): # load tag dictionary self.reset() i16 = self.i16 i32 = self.i32 for i in range(i16(fp.read(2))): ifd = fp.read(12) tag, typ = i16(ifd), i16(ifd, 2) if Image.DEBUG: import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") print "tag: %s (%d)" % (tagname, tag), print "- type: %s (%d)" % (typname, typ), try: dispatch = self.load_dispatch[typ] except KeyError: if Image.DEBUG: print "- unsupported type", typ continue # ignore unsupported type size, handler = dispatch size = size * i32(ifd, 4) # Get and expand tag value if size > 4: here = fp.tell() fp.seek(i32(ifd, 8)) data = ImageFile._safe_read(fp, size) fp.seek(here) else: data = ifd[8 : 8 + size] if len(data) != size: raise IOError, "not enough data" self.tagdata[tag] = typ, data if Image.DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK): print "- value: <table: %d bytes>" % size else: print "- value:", self[tag] self.next = i32(fp.read(4))
def chunk_zTXt(self, pos, len): # compressed text s = ImageFile._safe_read(self.fp, len) k, v = string.split(s, "\0", 1) comp_method = ord(v[0]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:]) return s
def chunk_tEXt(self, pos, len): # text s = ImageFile._safe_read(self.fp, len) try: k, v = string.split(s, "\0", 1) except ValueError: k = s; v = "" # fallback for broken tEXt tags if k: self.im_info[k] = self.im_text[k] = v return s
def chunk_pHYs(self, pos, length): # pixels per unit s = ImageFile._safe_read(self.fp, length) px, py = i32(s), i32(s[4:]) unit = i8(s[8]) if unit == 1: # meter dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) self.im_info["dpi"] = dpi elif unit == 0: self.im_info["aspect"] = px, py return s
def chunk_tRNS(self, pos, len): # transparency s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": i = string.find(s, chr(0)) if i >= 0: self.im_info["transparency"] = i elif self.im_mode == "L": self.im_info["transparency"] = i16(s) elif self.im_mode == "RGB": self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s
def chunk_IHDR(self, pos, length): # image header s = ImageFile._safe_read(self.fp, length) self.im_size = i32(s), i32(s[4:]) try: self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: pass if i8(s[12]): self.im_info["interlace"] = 1 if i8(s[11]): raise SyntaxError("unknown filter category") return s
def chunk_tRNS(self, pos, len): # transparency s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i self.im_info["transparency_palette"] = s elif self.im_mode == "L": self.im_info["transparency"] = i16(s) elif self.im_mode == "RGB": self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s
def chunk_tEXt(self, pos, len): # text s = ImageFile._safe_read(self.fp, len) try: k, v = s.split(b"\0", 1) except ValueError: k = s; v = b"" # fallback for broken tEXt tags if k: if bytes is not str: k = k.decode('latin-1', 'strict') v = v.decode('latin-1', 'replace') self.im_info[k] = self.im_text[k] = v return s
def verify(self, endchunk=b"IEND"): # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. cids = [] while True: cid, pos, length = self.read() if cid == endchunk: break self.crc(cid, ImageFile._safe_read(self.fp, length)) cids.append(cid) return cids
def verify(self, endchunk = b"IEND"): # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. cids = [] while True: cid, pos, len = self.read() if cid == endchunk: break self.crc(cid, ImageFile._safe_read(self.fp, len)) cids.append(cid) return cids
def chunk_tRNS(self, pos, length): # transparency s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": if _simple_palette.match(s): i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i else: self.im_info["transparency"] = s elif self.im_mode == "L": self.im_info["transparency"] = i16(s) elif self.im_mode == "RGB": self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s
def _open(self): if self.fp.read(8) != _MAGIC: raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk self.png = PngStream(self.fp) while True: # # get next chunk cid, pos, length = self.png.read() try: s = self.png.call(cid, pos, length) except EOFError: break except AttributeError: if Image.DEBUG: print(cid, pos, length, "(unknown)") s = ImageFile._safe_read(self.fp, length) self.png.crc(cid, s) # # Copy relevant attributes from the PngStream. An alternative # would be to let the PngStream class modify these attributes # directly, but that introduces circular references which are # difficult to break if things go wrong in the decoder... # (believe me, I've tried ;-) self.mode = self.png.im_mode self.size = self.png.im_size self.info = self.png.im_info self.text = self.png.im_text # experimental self.tile = self.png.im_tile if self.png.im_palette: rawmode, data = self.png.im_palette self.palette = ImagePalette.raw(rawmode, data) self.__idat = length # used by load_read()
def SOF(self, marker): # # Start of frame marker. Defines the size and mode of the # image. JPEG is colour blind, so we use some simple # heuristics to map the number of layers to an appropriate # mode. Note that this could be made a bit brighter, by # looking for JFIF and Adobe APP markers. n = i16(self.fp.read(2)) - 2 s = ImageFile._safe_read(self.fp, n) self.size = i16(s[3:]), i16(s[1:]) self.bits = i8(s[0]) if self.bits != 8: raise SyntaxError("cannot handle %d-bit layers" % self.bits) self.layers = i8(s[5]) if self.layers == 1: self.mode = "L" elif self.layers == 3: self.mode = "RGB" elif self.layers == 4: self.mode = "CMYK" else: raise SyntaxError("cannot handle %d-layer images" % self.layers) if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: self.info["progressive"] = self.info["progression"] = 1 if self.icclist: # fixup icc profile self.icclist.sort() # sort by sequence number if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) icc_profile = b"".join(profile) else: icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile self.icclist = None for i in range(6, len(s), 3): t = s[i:i + 3] # 4-tuples: id, vsamp, hsamp, qtable self.layer.append((t[0], i8(t[1]) // 16, i8(t[1]) & 15, i8(t[2])))
def _open(self): if self.fp.read(8) != _MAGIC: raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk self.png = PngStream(self.fp) while True: # # get next chunk cid, pos, len = self.png.read() try: s = self.png.call(cid, pos, len) except EOFError: break except AttributeError: if Image.DEBUG: print(cid, pos, len, "(unknown)") s = ImageFile._safe_read(self.fp, len) self.png.crc(cid, s) # # Copy relevant attributes from the PngStream. An alternative # would be to let the PngStream class modify these attributes # directly, but that introduces circular references which are # difficult to break if things go wrong in the decoder... # (believe me, I've tried ;-) self.mode = self.png.im_mode self.size = self.png.im_size self.info = self.png.im_info self.text = self.png.im_text # experimental self.tile = self.png.im_tile if self.png.im_palette: rawmode, data = self.png.im_palette self.palette = ImagePalette.raw(rawmode, data) self.__idat = len # used by load_read()
def chunk_zTXt(self, pos, len): # compressed text s = ImageFile._safe_read(self.fp, len) k, v = s.split(b"\0", 1) comp_method = i8(v[0]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib v = zlib.decompress(v[1:]) if bytes is not str: k = k.decode('latin-1', 'strict') v = v.decode('latin-1', 'replace') self.im_info[k] = self.im_text[k] = v return s
def SOF(self, marker): # # Start of frame marker. Defines the size and mode of the # image. JPEG is colour blind, so we use some simple # heuristics to map the number of layers to an appropriate # mode. Note that this could be made a bit brighter, by # looking for JFIF and Adobe APP markers. n = i16(self.fp.read(2)) - 2 s = ImageFile._safe_read(self.fp, n) self.size = i16(s[3:]), i16(s[1:]) self.bits = i8(s[0]) if self.bits != 8: raise SyntaxError("cannot handle %d-bit layers" % self.bits) self.layers = i8(s[5]) if self.layers == 1: self.mode = "L" elif self.layers == 3: self.mode = "RGB" elif self.layers == 4: self.mode = "CMYK" else: raise SyntaxError("cannot handle %d-layer images" % self.layers) if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: self.info["progressive"] = self.info["progression"] = 1 if self.icclist: # fixup icc profile self.icclist.sort() # sort by sequence number if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) icc_profile = b"".join(profile) else: icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile self.icclist = None for i in range(6, len(s), 3): t = s[i : i + 3] # 4-tuples: id, vsamp, hsamp, qtable self.layer.append((t[0], i8(t[1]) // 16, i8(t[1]) & 15, i8(t[2])))
def chunk_zTXt(self, pos, len): # compressed text s = ImageFile._safe_read(self.fp, len) k, v = s.split(b"\0", 1) comp_method = i8(v[0]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib v = zlib.decompress(v[1:]) if bytes is not str: k = k.decode("latin-1", "strict") v = v.decode("latin-1", "replace") self.im_info[k] = self.im_text[k] = v return s
def APP(self, marker): # # Application marker. Store these in the APP dictionary. # Also look for well-known application markers. n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) app = "APP%d" % (marker&15) self.app[app] = s # compatibility self.applist.append((app, s)) if marker == 0xFFE0 and s[:4] == "JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: jfif_unit = ord(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass else: if jfif_unit == 1: self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density elif marker == 0xFFE1 and s[:5] == "Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:5] == "FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change elif marker == 0xFFEE and s[:5] == "Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: adobe_transform = ord(s[1]) except: pass else: self.info["adobe_transform"] = adobe_transform
def chunk_tEXt(self, pos, length): # text s = ImageFile._safe_read(self.fp, length) try: k, v = s.split(b"\0", 1) except ValueError: # fallback for broken tEXt tags k = s v = b"" if k: if bytes is not str: k = k.decode("latin-1", "strict") v = v.decode("latin-1", "replace") self.im_info[k] = self.im_text[k] = v self.check_text_memory(len(v)) return s
def APP(self, marker): # # Application marker. Store these in the APP dictionary. # Also look for well-known application markers. n = i16(self.fp.read(2)) - 2 s = ImageFile._safe_read(self.fp, n) app = "APP%d" % (marker & 15) self.app[app] = s # compatibility self.applist.append((app, s)) if marker == 0xFFE0 and s[:4] == "JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: jfif_unit = ord(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass else: if jfif_unit == 1: self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density elif marker == 0xFFE1 and s[:5] == "Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:5] == "FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change elif marker == 0xFFEE and s[:5] == "Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: adobe_transform = ord(s[1]) except: pass else: self.info["adobe_transform"] = adobe_transform
def verify(self, endchunk=b"IEND"): # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. cids = [] while True: try: cid, pos, length = self.read() except struct.error: raise IOError("truncated PNG file") if cid == endchunk: break self.crc(cid, ImageFile._safe_read(self.fp, length)) cids.append(cid) return cids
def DQT(self, marker): # # Define quantization table. Support baseline 8-bit tables # only. Note that there might be more than one table in # each marker. # FIXME: The quantization tables can be used to estimate the # compression quality. n = i16(self.fp.read(2)) - 2 s = ImageFile._safe_read(self.fp, n) while len(s): if len(s) < 65: raise SyntaxError("bad quantization table marker") v = i8(s[0]) if v // 16 == 0: self.quantization[v & 15] = array.array("b", s[1:65]) s = s[65:] else: return # FIXME: add code to read 16-bit tables!
def chunk_tRNS(self, pos, length): # transparency s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": if _simple_palette.match(s): # tRNS contains only one full-transparent entry, # other entries are full opaque i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i else: # otherwise, we have a byte string with one alpha value # for each palette entry self.im_info["transparency"] = s elif self.im_mode == "L": self.im_info["transparency"] = i16(s) elif self.im_mode == "RGB": self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s
def chunk_iTXt(self, pos, length): # international text r = s = ImageFile._safe_read(self.fp, length) try: k, r = r.split(b"\0", 1) except ValueError: return s if len(r) < 2: return s cf, cm, r = i8(r[0]), i8(r[1]), r[2:] try: lang, tk, v = r.split(b"\0", 2) except ValueError: return s if cf != 0: if cm == 0: try: v = _safe_zlib_decompress(v) except ValueError: if ImageFile.LOAD_TRUNCATED_IMAGES: return s else: raise except zlib.error: return s else: return s if bytes is not str: try: k = k.decode("latin-1", "strict") lang = lang.decode("utf-8", "strict") tk = tk.decode("utf-8", "strict") v = v.decode("utf-8", "strict") except UnicodeError: return s self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) self.check_text_memory(len(v)) return s
def chunk_iCCP(self, pos, len): # ICC profile s = ImageFile._safe_read(self.fp, len) # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) i = s.find(b"\0") if Image.DEBUG: print("iCCP profile name", s[:i]) print("Compression method", i8(s[i])) comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: icc_profile = zlib.decompress(s[i+2:]) except zlib.error: icc_profile = None # FIXME self.im_info["icc_profile"] = icc_profile return s
def chunk_iCCP(self, pos, length): # ICC profile s = ImageFile._safe_read(self.fp, length) # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) i = s.find(b"\0") logger.debug("iCCP profile name %s", s[:i]) logger.debug("Compression method %s", i8(s[i])) comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: icc_profile = _safe_zlib_decompress(s[i+2:]) except zlib.error: icc_profile = None # FIXME self.im_info["icc_profile"] = icc_profile return s
def _read_bitmap(self, offset): s = self.fp.read(4) s += ImageFile._safe_read(self.fp, _i32(s) - 4) if len(s) not in (40, 108, 124): # Only accept BMP v3, v4, and v5. raise IOError("Unsupported BMP header type (%d)" % len(s)) bpp = _i16(s[14:]) if bpp != 32: # Only accept BMP with alpha. raise IOError("Unsupported BMP pixel depth (%d)" % bpp) compression = _i32(s[16:]) if compression == 3: # BI_BITFIELDS compression mask = (_i32(self.fp.read(4)), _i32(self.fp.read(4)), _i32(self.fp.read(4)), _i32(self.fp.read(4))) # XXX Handle mask. elif compression != 0: # Only accept uncompressed BMP. raise IOError("Unsupported BMP compression (%d)" % compression) self.mode, rawmode = 'RGBA', 'BGRA' self._size = (_i32(s[4:]), _i32(s[8:])) direction = -1 if s[11] == '\xff': # upside-down storage self._size = self.size[0], 2**32 - self.size[1] direction = 0 self.info["compression"] = compression # data descriptor self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, 0, direction))]
def Skip(self, marker): n = i16(self.fp.read(2)) - 2 ImageFile._safe_read(self.fp, n)
def _bitmap(self, header=0, offset=0): if header: self.fp.seek(header) read = self.fp.read if not offset: offset = self.fp.tell() # CORE/INFO s = read(4) s = s + ImageFile._safe_read(self.fp, i32(s)-4) if len(s) == 12: # OS/2 1.0 CORE bits = i16(s[10:]) self.size = i16(s[4:]), i16(s[6:]) compression = 0 lutsize = 3 colors = 0 direction = -1 elif len(s) in [40, 64, 108, 124]: # WIN 3.1 or OS/2 2.0 INFO bits = i16(s[14:]) self.size = i32(s[4:]), i32(s[8:]) compression = i32(s[16:]) pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter lutsize = 4 colors = i32(s[32:]) direction = -1 if i8(s[11]) == 0xff: # upside-down storage self.size = self.size[0], 2**32 - self.size[1] direction = 0 self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), pxperm)) else: raise IOError("Unsupported BMP header type (%d)" % len(s)) if (self.size[0]*self.size[1]) > 2**31: # Prevent DOS for > 2gb images raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) if not colors: colors = 1 << bits # MODE try: self.mode, rawmode = BIT2MODE[bits] except KeyError: raise IOError("Unsupported BMP pixel depth (%d)" % bits) if compression == 3: # BI_BITFIELDS compression mask = i32(s[0x36-14:]), i32(s[0x3a-14:]), i32(s[0x3E-14:]), i32(s[0x42-14:]) if bits == 32 and mask == (0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000): rawmode = "BGRA" elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): rawmode = "BGR;16" elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): rawmode = "BGR;15" else: print bits, map(hex, mask) raise IOError("Unsupported BMP bitfields layout") elif compression != 0: raise IOError("Unsupported BMP compression (%d)" % compression) # LUT if self.mode == "P": palette = [] greyscale = 1 if colors == 2: indices = (0, 255) elif colors > 2**16 or colors <= 0: # We're reading a i32. raise IOError("Unsupported BMP Palette size (%d)" % colors) else: indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] if rgb != o8(i)*3: greyscale = 0 palette.append(rgb) if greyscale: if colors == 2: self.mode = rawmode = "1" else: self.mode = rawmode = "L" else: self.mode = "P" self.palette = ImagePalette.raw( "BGR", b"".join(palette) ) if not offset: offset = self.fp.tell() self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), direction))] self.info["compression"] = compression
def _bitmap(self, header=0, offset=0): if header: self.fp.seek(header) read = self.fp.read # CORE/INFO s = read(4) s = s + ImageFile._safe_read(self.fp, i32(s) - 4) if len(s) == 12: # OS/2 1.0 CORE bits = i16(s[10:]) self.size = i16(s[4:]), i16(s[6:]) compression = 0 lutsize = 3 colors = 0 direction = -1 elif len(s) in [40, 64]: # WIN 3.1 or OS/2 2.0 INFO bits = i16(s[14:]) self.size = i32(s[4:]), i32(s[8:]) compression = i32(s[16:]) lutsize = 4 colors = i32(s[32:]) direction = -1 if i8(s[11]) == 0xff: # upside-down storage self.size = self.size[0], 2**32 - self.size[1] direction = 0 else: raise IOError("Unsupported BMP header type (%d)" % len(s)) if not colors: colors = 1 << bits # MODE try: self.mode, rawmode = BIT2MODE[bits] except KeyError: raise IOError("Unsupported BMP pixel depth (%d)" % bits) if compression == 3: # BI_BITFIELDS compression mask = i32(read(4)), i32(read(4)), i32(read(4)) if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): rawmode = "BGRX" elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): rawmode = "BGR;16" elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): rawmode = "BGR;15" else: # print bits, map(hex, mask) raise IOError("Unsupported BMP bitfields layout") elif compression != 0: raise IOError("Unsupported BMP compression (%d)" % compression) # LUT if self.mode == "P": palette = [] greyscale = 1 if colors == 2: indices = (0, 255) else: indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] if rgb != o8(i) * 3: greyscale = 0 palette.append(rgb) if greyscale: if colors == 2: self.mode = rawmode = "1" else: self.mode = rawmode = "L" else: self.mode = "P" self.palette = ImagePalette.raw("BGR", b"".join(palette)) if not offset: offset = self.fp.tell() self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, ((self.size[0] * bits + 31) >> 3) & (~3), direction))] self.info["compression"] = compression
def APP(self, marker): # # Application marker. Store these in the APP dictionary. # Also look for well-known application markers. n = i16(self.fp.read(2)) - 2 s = ImageFile._safe_read(self.fp, n) app = "APP%d" % (marker & 15) self.app[app] = s # compatibility self.applist.append((app, s)) if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: jfif_unit = i8(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass else: if jfif_unit == 1: self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": # Since an ICC profile can be larger than the maximum size of # a JPEG marker (64K), we need provisions to split it into # multiple markers. The format defined by the ICC specifies # one or more APP2 markers containing the following data: # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) # Marker sequence number 1, 2, etc (1 byte) # Number of markers Total of APP2's used (1 byte) # Procfile data (remainder of APP2 data) # Decoders should use the marker sequence numbers to # reassemble the profile, rather than assuming that the APP2 # markers appear in the correct sequence. self.icclist.append(s) elif marker == 0xFFEE and s[:5] == b"Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: adobe_transform = i8(s[1]) except: pass else: self.info["adobe_transform"] = adobe_transform elif marker == 0xFFE2 and s[:4] == b"MPF\0": # extract MPO information self.info["mp"] = s[4:] # offset is current location minus buffer size # plus constant header size self.info["mpoffset"] = self.fp.tell() - n + 4
def chunk_gAMA(self, pos, length): # gamma setting s = ImageFile._safe_read(self.fp, length) self.im_info["gamma"] = i32(s) / 100000.0 return s
def load(self, fp): # load tag dictionary self.reset() self.offset = fp.tell() i16 = self.i16 i32 = self.i32 for i in range(i16(fp.read(2))): ifd = fp.read(12) tag, typ = i16(ifd), i16(ifd, 2) if Image.DEBUG: from PIL import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") print("tag: %s (%d)" % (tagname, tag), end=' ') print("- type: %s (%d)" % (typname, typ), end=' ') try: dispatch = self.load_dispatch[typ] except KeyError: if Image.DEBUG: print("- unsupported type", typ) continue # ignore unsupported type size, handler = dispatch size = size * i32(ifd, 4) # Get and expand tag value if size > 4: here = fp.tell() if Image.DEBUG: print("Tag Location: %s" % here) fp.seek(i32(ifd, 8)) if Image.DEBUG: print("Data Location: %s" % fp.tell()) data = ImageFile._safe_read(fp, size) fp.seek(here) else: data = ifd[8:8 + size] if len(data) != size: warnings.warn("Possibly corrupt EXIF data. " "Expecting to read %d bytes but only got %d. " "Skipping tag %s" % (size, len(data), tag)) continue self.tagdata[tag] = data self.tagtype[tag] = typ if Image.DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): print("- value: <table: %d bytes>" % size) else: print("- value:", self[tag]) self.next = i32(fp.read(4))
def _bitmap(self, header=0, offset=0): if header: self.fp.seek(header) read = self.fp.read s = read(4) s = s + ImageFile._safe_read(self.fp, i32(s) - 4) if len(s) == 12: bits = i16(s[10:]) self.size = i16(s[4:]), i16(s[6:]) compression = 0 lutsize = 3 colors = 0 direction = -1 elif len(s) in [40, 64, 108, 124]: bits = i16(s[14:]) self.size = i32(s[4:]), i32(s[8:]) compression = i32(s[16:]) pxperm = (i32(s[24:]), i32(s[28:])) lutsize = 4 colors = i32(s[32:]) direction = -1 if i8(s[11]) == 0xff: self.size = self.size[0], 2**32 - self.size[1] direction = 0 self.info["dpi"] = tuple( map(lambda x: math.ceil(x / 39.3701), pxperm)) else: raise IOError("Unsupported BMP header type (%d)" % len(s)) if (self.size[0] * self.size[1]) > 2**31: raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) if not colors: colors = 1 << bits try: self.mode, rawmode = BIT2MODE[bits] except KeyError: raise IOError("Unsupported BMP pixel depth (%d)" % bits) if compression == 3: mask = i32(read(4)), i32(read(4)), i32(read(4)) if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): rawmode = "BGRX" elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): rawmode = "BGR;16" elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): rawmode = "BGR;15" else: raise IOError("Unsupported BMP bitfields layout") elif compression != 0: raise IOError("Unsupported BMP compression (%d)" % compression) if self.mode == "P": palette = [] greyscale = 1 if colors == 2: indices = (0, 255) elif colors > 2**16 or colors <= 0: raise IOError("Unsupported BMP Palette size (%d)" % colors) else: indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] if rgb != o8(i) * 3: greyscale = 0 palette.append(rgb) if greyscale: if colors == 2: self.mode = rawmode = "1" else: self.mode = rawmode = "L" else: self.mode = "P" self.palette = ImagePalette.raw("BGR", b"".join(palette)) if not offset: offset = self.fp.tell() self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, ((self.size[0] * bits + 31) >> 3) & (~3), direction))] self.info["compression"] = compression
def _bitmap(self, header=0, offset=0): """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek if header: seek(header) file_info = dict() file_info['header_size'] = i32( read(4) ) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 # --------------------- If requested, read header at a specific position header_data = ImageFile._safe_read( self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size # --------------------------------------------------- IBM OS/2 Bitmap v1 # ------ This format has different offsets because of width/height types if file_info['header_size'] == 12: file_info['width'] = i16(header_data[0:2]) file_info['height'] = i16(header_data[2:4]) file_info['planes'] = i16(header_data[4:6]) file_info['bits'] = i16(header_data[6:8]) file_info['compression'] = self.RAW file_info['palette_padding'] = 3 # ---------------------------------------------- Windows Bitmap v2 to v5 elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 if file_info['header_size'] >= 40: # v3 and OS/2 file_info['y_flip'] = i8(header_data[7]) == 0xff file_info['direction'] = 1 if file_info['y_flip'] else -1 file_info['width'] = i32(header_data[0:4]) file_info['height'] = i32( header_data[4:8] ) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) file_info['planes'] = i16(header_data[8:10]) file_info['bits'] = i16(header_data[10:12]) file_info['compression'] = i32(header_data[12:16]) file_info['data_size'] = i32( header_data[16:20]) # byte size of pixel data file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 self.info["dpi"] = tuple( map(lambda x: int(math.ceil(x / 39.3701)), file_info['pixels_per_meter'])) if file_info['compression'] == self.BITFIELDS: if len(header_data) >= 52: for idx, mask in enumerate( ['r_mask', 'g_mask', 'b_mask', 'a_mask']): file_info[mask] = i32(header_data[36 + idx * 4:40 + idx * 4]) else: for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']: file_info[mask] = i32(read(4)) file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) else: raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) # ------------------ Special case : header is reported 40, which # ---------------------- is shorter than real size for bpp >= 16 self.size = file_info['width'], file_info['height'] # -------- If color count was not found in the header, compute from bits file_info['colors'] = file_info['colors'] if file_info.get( 'colors', 0) else (1 << file_info['bits']) # -------------------------------- Check abnormal values for DOS attacks if file_info['width'] * file_info['height'] > 2**31: raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) # ----------------------- Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) # ----------------- Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: SUPPORTED = { 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] } MASK_MODES = { (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15" } if file_info['bits'] in SUPPORTED: if file_info['bits'] == 32 and file_info[ 'rgba_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] self.mode = "RGBA" if raw_mode in ("BGRA", ) else self.mode elif file_info['bits'] in ( 24, 16 ) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] else: raise IOError("Unsupported BMP bitfields layout") else: raise IOError("Unsupported BMP bitfields layout") elif file_info['compression'] == self.RAW: if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" else: raise IOError("Unsupported BMP compression (%d)" % file_info['compression']) # ---------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images # ----------------------------------------------------- 1-bit images if not (0 < file_info['colors'] <= 65536): raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors']) else: padding = file_info['palette_padding'] palette = read(padding * file_info['colors']) greyscale = True indices = (0, 255) if file_info['colors'] == 2 else list( range(file_info['colors'])) # ------------------ Check if greyscale and ignore palette if so for ind, val in enumerate(indices): rgb = palette[ind * padding:ind * padding + 3] if rgb != o8(val) * 3: greyscale = False # -------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info['colors'] == 2 else "L" raw_mode = self.mode else: self.mode = "P" self.palette = ImagePalette.raw( "BGRX" if padding == 4 else "BGR", palette) # ----------------------------- Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] self.tile = [ ('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(), (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction'])) ]