"""
TIFF image parser.

Author: Victor Stinner
Creation date: 30 september 2006
"""

from hachoir_parser import Parser
from hachoir_core.field import (FieldSet, ParserError,
    UInt16, UInt32, String)
from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
from hachoir_parser.image.exif import BasicIFDEntry
from hachoir_core.tools import createDict

MAX_COUNT = 250

class IFDEntry(BasicIFDEntry):
    static_size = 12*8

    TAG_INFO = {
        254: ("new_subfile_type", "New subfile type"),
        256: ("img_width", "Image width in pixels"),
        257: ("img_height", "Image height in pixels"),
        258: ("bits_per_sample", "Bits per sample"),
        259: ("compression", "Compression method"),
        262: ("photo_interpret", "Photometric interpretation"),
        263: ("thres", "Thresholding"),
        264: ("cell_width", "Cellule width"),
        265: ("cell_height", "Cellule height"),
        266: ("fill_order", "Fill order"),
        269: ("doc_name", "Document name"),
        270: ("description", "Image description"),
        271: ("make", "Make"),
        272: ("model", "Model"),
        273: ("strip_ofs", "Strip offsets"),
        274: ("orientation", "Orientation"),
        277: ("sample_pixel", "Samples per pixel"),
        278: ("row_per_strip", "Rows per strip"),
        279: ("strip_byte", "Strip byte counts"),
        280: ("min_sample_value", "Min sample value"),
        281: ("max_sample_value", "Max sample value"),
        282: ("xres", "X resolution"),
        283: ("yres", "Y resolution"),
        284: ("planar_conf", "Planar configuration"),
        285: ("page_name", "Page name"),
        286: ("xpos", "X position"),
        287: ("ypos", "Y position"),
        288: ("free_ofs", "Free offsets"),
        289: ("free_byte", "Free byte counts"),
        290: ("gray_resp_unit", "Gray response unit"),
        291: ("gray_resp_curve", "Gray response curve"),
        292: ("group3_opt", "Group 3 options"),
        293: ("group4_opt", "Group 4 options"),
        296: ("res_unit", "Resolution unit"),
        297: ("page_nb", "Page number"),
        301: ("color_respt_curve", "Color response curves"),
        305: ("software", "Software"),
        306: ("date_time", "Date time"),
        307: ("artist", "Artist"),
        308: ("host_computer", "Host computer"),
        317: ("predicator", "Predicator"),
        318: ("white_pt", "White point"),
        319: ("prim_chomat", "Primary chromaticities"),
        320: ("color_map", "Color map"),
    }
    TAG_NAME = createDict(TAG_INFO, 0)

    def __init__(self, *args):
        FieldSet.__init__(self, *args)
        tag = self["tag"].value
        if tag in self.TAG_INFO:
            self._name, self._description = self.TAG_INFO[tag]
        else:
            self._parser = None

class IFD(FieldSet):
    def __init__(self, *args):
        FieldSet.__init__(self, *args)
        self._size = 16 + self["count"].value * IFDEntry.static_size

    def createFields(self):
        yield UInt16(self, "count")
        if MAX_COUNT < self["count"].value:
            raise ParserError("TIFF IFD: Invalid count (%s)"
                % self["count"].value)
        for index in xrange(self["count"].value):
            yield IFDEntry(self, "entry[]")

class TiffFile(Parser):
    PARSER_TAGS = {
        "id": "tiff",
        "category": "image",
        "file_ext": ("tif", "tiff"),
        "mime": (u"image/tiff",),
        "min_size": 8*8,
# TODO: Re-enable magic
#        "magic": (("II\x2A\0", 0), ("MM\0\x2A", 0)),
        "description": "TIFF picture"
    }

    # Correct endian is set in constructor
    endian = LITTLE_ENDIAN
#    endian = BIG_ENDIAN

    def validate(self):
        endian = self.stream.readBytes(0, 2)
        if endian == "II":
            self.endian = LITTLE_ENDIAN
        elif endian == "MM":
            self.endian = BIG_ENDIAN
        else:
            return "Invalid endian"
        if self["version"].value != 42:
            return "Unknown TIFF version"
        if self["img_dir_ofs"].value % 2:
            return "Invalid first image directory offset"
        return True

    def createFields(self):
        yield String(self, "endian", 2, 'Endian ("II" or "MM")', charset="ASCII")
        yield UInt16(self, "version", "TIFF version number")
        yield UInt32(self, "img_dir_ofs", "First image directory offset (in bytes from the beginning)")

        raw = self.seekByte(self["img_dir_ofs"].value, relative=False)
        if raw:
            yield raw

        yield IFD(self, "ifd")

        if self.current_size < self._size:
            yield self.seekBit(self._size, "end")



syntax highlighted by Code2HTML, v. 0.9.1