Ejemplo n.º 1
0
 def handle_chunk_PLTE(self, chunk, length):
     # http://www.w3.org/TR/PNG/#11PLTE
     if length % 3 != 0:
         raise PNGReaderError(
             "PLTE chunk's length must be a multiple of 3.")
     if length > (2**self.bit_depth) * 3:
         raise PNGReaderError("PLTE chunk is too long.")
     if length == 0:
         raise PNGReaderError("Empty PLTE is not allowed.")
     # TODO: might be faster if we don't group, but makes the palette
     # interface harder to use
     self.plte = chunk
Ejemplo n.º 2
0
 def validate_signature(self):
     """
     Validates the signature, should only ever be called once.
     """
     signature = self.fileobj.read(8)
     if signature != PNG_SIGNATURE:
         raise PNGReaderError("PNG file has invalid signature.")
Ejemplo n.º 3
0
 def handle_chunk_tRNS(self, chunk, length):
     self.trns = chunk
     if self.colormap:
         if not self.palette:
             warnings.warn("PLTE chunk is required before tRNS chunk.")
         else:
             if length > len(self.palette):
                 # Was warning, but promoted to Error as it
                 # would otherwise cause pain later on.
                 raise PNGReaderError("tRNS chunk is too long.")
     else:
         if self.alpha:
             raise PNGReaderError(
                 "tRNS chunk is not valid with colour type %d." %
                 self.color_type)
         try:
             self.transparent = struct.unpack("!%dH" % self.color_planes,
                                              chunk)
         except struct.error:
             raise PNGReaderError("tRNS chunk has incorrect length.")
Ejemplo n.º 4
0
 def handle_chunk_bKGD(self, chunk, length):
     try:
         if self.colormap:
             if not self.palette:
                 warnings.warn("PLTE chunk is required before bKGD chunk.")
             self.background = struct.unpack('B', chunk)
         else:
             self.background = struct.unpack("!%dH" % self.color_planes,
                                             chunk)
     except struct.error:
         raise PNGReaderError("bKGD chunk has incorrect length.")
Ejemplo n.º 5
0
    def iter_chunks(self):
        while not self.done_reading:
            # chunk_length
            raw_chunk_length = self.fileobj.read(4)
            if not raw_chunk_length:
                raise NoChunkLength()
            if len(raw_chunk_length) != 4:
                raise InvalidChunkLength(raw_chunk_length)
            chunk_length = struct.unpack('!I', raw_chunk_length)[0]

            # chunk type
            raw_chunk_type = self.fileobj.read(4)
            if not raw_chunk_type:
                raise NoChunkType()
            if len(raw_chunk_type) != 4:
                raise InvalidChunkType(raw_chunk_type)
            bytes_chunk_type = struct.unpack('!4s', raw_chunk_type)[0]
            chunk_type = bytestostr(bytes_chunk_type)
            # sanity check
            if chunk_length > MAX_CHUNK_LENGTH:
                raise PNGReaderError('Chunk %s is too large: %d.' %
                                     (chunk_type, chunk_length))

            # read data
            data = self.fileobj.read(chunk_length)
            if len(data) != chunk_length:
                raise ChunkError('Chunk %s too short for required %i octets.' %
                                 (chunk_type, chunk_length))

            # read checksum
            checksum = self.fileobj.read(4)
            if len(checksum) != 4:
                raise ChunkError('Chunk %s checksum too short.' % chunk_type)

            verify = zlib.crc32(bytes_chunk_type)
            verify = zlib.crc32(data, verify)
            # Whether the output from zlib.crc32 is signed or not varies
            # according to hideous implementation details, see
            # http://bugs.python.org/issue1202 .
            # We coerce it to be positive here (in a way which works on
            # Python 2.3 and older).
            verify &= VERIFY_CONSTANT
            verify = struct.pack('!I', verify)
            if checksum != verify:
                # print repr(checksum)
                (a, ) = struct.unpack('!I', checksum)
                (b, ) = struct.unpack('!I', verify)
                raise ChunkError(
                    "Checksum error in %s chunk: 0x%08X != 0x%08X." %
                    (chunk_type, a, b))

            yield chunk_type, chunk_length, data
Ejemplo n.º 6
0
 def handle_chunk_sBIT(self, chunk, length):
     self.sbit = chunk
     if (self.colormap and length != 3
             or not self.colormap and length != self.planes):
         raise PNGReaderError("sBIT chunk has incorrect length.")
Ejemplo n.º 7
0
 def handle_chunk_gAMA(self, chunk, length):
     try:
         self.gamma = struct.unpack("!L", chunk)[0] / 100000.0
     except struct.error:
         raise PNGReaderError("gAMA chunk has incorrect length.")
Ejemplo n.º 8
0
    def handle_chunk_IHDR(self, chunk, length):
        # http://www.w3.org/TR/PNG/#11IHDR
        if length != 13:
            raise ChunkError(
                'IHDR chunk has incorrect length %s, should be 13.' % length)
        (self.width, self.height, self.bit_depth, self.color_type,
         self.compression_method, self.filter_method,
         self.interlace_method) = struct.unpack("!2I5B", chunk)

        # Check that the header specifies only valid combinations.
        if self.bit_depth not in ALLOWED_BIT_DEPTHS:
            raise PNGReaderError("invalid bit depth %d" % self.bit_depth)

        if self.color_type not in ALLOWED_COLOR_TYPES:
            raise PNGReaderError("invalid colour type %d" % self.color_type)

        # Check indexed (palettized) images have 8 or fewer bits
        # per pixel; check only indexed or greyscale images have
        # fewer than 8 bits per pixel.
        if ((self.color_type & 1 and self.bit_depth > 8)
                or (self.bit_depth < 8 and self.color_type not in (0, 3))):
            raise PNGReaderError(
                "Illegal combination of bit depth (%d)"
                " and colour type (%d)."
                " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." %
                (self.bit_depth, self.color_type))
        if self.compression_method != 0:
            raise PNGReaderError("unknown compression method %d" %
                                 self.compression_method)
        if self.filter_method != 0:
            raise PNGReaderError(
                "Unknown filter method %d,"
                " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." %
                self.filter_method)
        if self.interlace_method not in (0, 1):
            raise PNGReaderError(
                "Unknown interlace method %d,"
                " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ."
                % self.interlace_method)

        self.pixelsize = {
            0: 1,
            2: 3,
            3: 1,
            4: 2,
            6: 4,
        }[self.color_type]

        # Derived values
        # http://www.w3.org/TR/PNG/#6Colour-values
        colormap = bool(self.color_type & 1)
        greyscale = not (self.color_type & 2)
        alpha = bool(self.color_type & 4)
        if greyscale or colormap:
            color_planes = 1
        else:
            color_planes = 3
        planes = color_planes + alpha

        self.colormap = colormap
        self.greyscale = greyscale
        self.alpha = alpha
        self.mode = RGBA if self.alpha else RGB
        self.color_planes = color_planes
        self.planes = planes
        self.psize = fdiv(self.bit_depth, 8) * planes
        if int(self.psize) == self.psize:
            self.psize = int(self.psize)
        self.filter_unit = max(1, self.psize)
        self.row_bytes = int(math.ceil(self.width * self.psize))
        # scanline stuff
        self.scanline = array.array('B')
        if self.bit_depth == 16:
            array_code = 'H'
        else:
            array_code = 'B'
        data = array.array(array_code,
                           [0] * self.width * self.height * self.pixelsize)
        self.pixels = get_pixel_array(data, self.width, self.height,
                                      self.pixelsize)
        if self.interlace_method:
            self.adam7 = Adam7(self)
            self.scanline_length = self.adam7.get_scanline_length()
            self._process_scanline = self._process_interlaced_scanline
        else:
            self.previous_scanline = None
            self.scanline_length = self.row_bytes + 1
            self.current_y = 0
            self._process_scanline = self._process_straightlaced_scanline