Пример #1
0
    def parse(cls, stream: IO) -> 'TiffHeader':
        tiff_header_offset = stream.tell()

        # read byte order

        byte_order = tools.read_bytes_strict(stream, 2)

        if byte_order[0] == byte_order[1] == 0x49:
            byte_order = ByteOrder.LITTLE_ENDIAN
        elif byte_order[0] == byte_order[1] == 0x4D:
            byte_order = ByteOrder.BIG_ENDIAN
        else:
            raise RuntimeError('invalid tiff header format')

        # check tiff header signature

        tiff_id = tools.read_bytes_strict(stream, 2)
        tiff_id = endianess.convert(tiff_id, byte_order=byte_order)
        if tiff_id != TiffHeader.ID:
            raise RuntimeError('invalid tiff header format')

        # read IFD0 offset

        ifd0_offset = tools.read_bytes_strict(stream, 4)
        ifd0_offset = endianess.convert(ifd0_offset, byte_order=byte_order)

        return TiffHeader(offset=tiff_header_offset,
                          byte_order=byte_order,
                          ifd0_offset=ifd0_offset)
Пример #2
0
    def load(self):
        if self.is_loaded:
            return

        self._stream.seek(self.value_offset)
        data = tools.read_bytes_strict(self._stream, self.count*self.field_type.byte_count)

        self._value = parse_value(data=data, count=self.count, field_type=self.field_type, byte_order=self._byte_order)
        self._is_loaded = True
Пример #3
0
def scan_jpeg_structure(stream: IO, include_eoi: bool) -> List[JpegSegment]:
    offset = stream.tell()

    check_jpeg_signature(stream)
    segment = JpegSegment.create(marker=SOI,
                                 stream=stream,
                                 offset=offset,
                                 size=JpegMarker.MARKER_SIZE)
    segment.log()
    structure = [segment]

    offset += JpegMarker.MARKER_SIZE

    # scan segments

    segment_marker = stream.read(JpegMarker.MARKER_SIZE)
    while len(segment_marker) == JpegMarker.MARKER_SIZE:
        segment_marker = endianess.convert_big_endian(segment_marker)
        segment_marker = JpegMarker.detect(segment_marker)

        if segment_marker == EOI:
            raise RuntimeError('unexpected EOI marker before SOS marker')

        segment_size = tools.read_bytes_strict(stream, JpegMarker.LENGTH_SIZE)
        segment_size = endianess.convert_big_endian(
            segment_size) + JpegMarker.MARKER_SIZE

        segment = JpegSegment.create(marker=segment_marker,
                                     stream=stream,
                                     offset=offset,
                                     size=segment_size)
        segment.log()
        structure.append(segment)

        offset += segment_size
        stream.seek(
            segment_size - JpegMarker.MARKER_SIZE - JpegMarker.LENGTH_SIZE,
            SEEK_CUR)

        if segment_marker == SOS:
            break

        segment_marker = stream.read(JpegMarker.MARKER_SIZE)

    if include_eoi:
        eoi_offset = scan_for_eoi(stream)
        if eoi_offset == 0:
            raise RuntimeError('EOI is not found')

        segment = JpegSegment.create(marker=EOI,
                                     stream=stream,
                                     offset=offset + eoi_offset,
                                     size=JpegMarker.MARKER_SIZE)
        segment.log()
        structure.append(segment)

    return structure
Пример #4
0
def parse_app_name(stream: IO) -> str:
    name = ''

    while True:
        byte = tools.read_bytes_strict(stream, 1)

        if byte[0] == 0x00:
            break

        name = name + byte.decode('ascii')

    return name
Пример #5
0
    def load(self):
        if self.is_loaded: return
        self._stream.seek(self.offset)

        tools.logger.debug(f'[{self.marker.name}] segment loading...')
        self._stream.seek(self.offset + JpegMarker.MARKER_SIZE + JpegMarker.LENGTH_SIZE)

        self._name = parse_app_name(self._stream)
        tools.logger.debug(f'-> name: {self._name}')

        if self._name.upper() == 'JFIF':
            self._is_loaded = True
            return

        # Exif or custom APP[2-15]
        byte = tools.read_bytes_strict(self._stream, 1)  # skip one more 0x00 byte ('Exif\0x00\0x00')
        if byte[0] != 0x00:
            raise RuntimeError('unexpected format of APP segment')

        # parse tiff header

        self._tiff_header = TiffHeader.parse(self._stream)
        tools.logger.debug(f'-> {self._tiff_header}')

        # parse IFD

        next_ifd_offset = self._tiff_header.offset + self._tiff_header.ifd0_offset
        next_offset_filed_used = False
        ifd = []
        while True:
            self._stream.seek(next_ifd_offset)
            tools.logger.debug(f'-> IDF #{len(ifd)}, offset=0x{next_ifd_offset:08X}')

            ifd_i = ImageFileDirectory.parse(self._stream, tiff_header=self._tiff_header)
            ifd.append(ifd_i)

            if next_offset_filed_used and ifd_i.next_ifd_offset == 0:
                break # the last ifd is reached

            if ifd_i.next_ifd_offset > 0:
                next_ifd_offset = self._tiff_header.offset + ifd_i.next_ifd_offset
                next_offset_filed_used = True
            else:
                assert ifd_i.offset + ifd_i.size <= self.offset + self.size, 'smth wrong with sizes'

                # some camera manufactures put IFDs one by one without next_ifd_offset
                if ifd_i.offset + ifd_i.size == self.offset + self.size:
                    break # the end of the segment is reached

                next_ifd_offset = ifd_i.offset + ifd_i.size

        self._ifd = tuple(ifd)
Пример #6
0
    def parse(cls, stream: IO, tiff_header: TiffHeader) -> 'IfdField':
        field_offset = stream.tell()

        tag_id = tools.read_bytes_strict(stream, 2)
        tag_id = endianess.convert(tag_id, byte_order=tiff_header.byte_order)

        type_id = tools.read_bytes_strict(stream, 2)
        type_id = endianess.convert(type_id, byte_order=tiff_header.byte_order)

        if FieldType.is_unknown(type_id):
            type_id = FieldType.Unknown
        else:
            type_id = FieldType(type_id)

        count = tools.read_bytes_strict(stream, 4)
        count = endianess.convert(count, byte_order=tiff_header.byte_order)

        field_size = count * type_id.byte_count
        if field_size <= 4:
            value_offset = field_offset + 8
            stream.seek(4, io.SEEK_CUR) # skip value data
            field_size = IfdField.FIXED_SIZE # no extra data outsize the field structure
        else:
            value_offset = tools.read_bytes_strict(stream, 4)
            value_offset = endianess.convert(value_offset, byte_order=tiff_header.byte_order)
            value_offset += tiff_header.offset
            field_size = tools.align4(field_size) + IfdField.FIXED_SIZE

        return IfdField(tag_id=tag_id,
                        count=count,
                        field_type=type_id,
                        stream=stream,
                        byte_order=tiff_header.byte_order,
                        value_offset=value_offset,
                        size=field_size,
                        offset=field_offset)
Пример #7
0
def check_jpeg_signature(stream: IO):
    marker = tools.read_bytes_strict(stream, JpegMarker.MARKER_SIZE)
    marker = endianess.convert_big_endian(marker)

    if marker != SOI.signature:
        raise RuntimeError('file is not JPEG')