func Parse(r io.Reader) (eIFD, gIFD, ioIFD tiff.IFD, err error) { rars := tiff.NewReadAtReadSeeker(r) var two [2]byte if _, err = rars.Read(two[:]); err != nil { return } if _, err = rars.Seek(0, 0); err != nil { return } switch string(two[:]) { case "MM", "II": // likely a tiff var t tiff.TIFF if t, err = tiff.Parse(rars, nil, nil); err != nil { return } for _, tIFD := range t.IFDs() { if tIFD.HasField(ExifIFDTagID) { eFld := tIFD.GetField(ExifIFDTagID) offset := eFld.Type().Valuer()(eFld.Value().Bytes(), eFld.Value().Order()).Uint() if eIFD, err = tiff.ParseIFD(t.R(), offset, ExifTagSpace, nil); err != nil { return } if tIFD.HasField(GPSIFDTagID) { gFld := tIFD.GetField(GPSIFDTagID) offset = gFld.Type().Valuer()(gFld.Value().Bytes(), gFld.Value().Order()).Uint() if gIFD, err = tiff.ParseIFD(t.R(), offset, GPSTagSpace, nil); err != nil { log.Printf("exif: GPS IFD found, but had trouble retrieving it from offset %d: %v\n", offset, err) } } if tIFD.HasField(InteroperabilityIFDTagID) { ioFld := tIFD.GetField(InteroperabilityIFDTagID) offset = ioFld.Type().Valuer()(ioFld.Value().Bytes(), ioFld.Value().Order()).Uint() if ioIFD, err = tiff.ParseIFD(t.R(), offset, IOPTagSpace, nil); err != nil { log.Printf("exif: IOP IFD found, but had trouble retrieving it from offset %d: %v\n", offset, err) } } return } } err = fmt.Errorf("exif: no exif ifd found in tiff") return case "\xff\xd8": // likely a jpeg err = fmt.Errorf("exif: still working on jpeg support") return } // Anything else is currently unsupported. err = fmt.Errorf("exif: unsupported header: %q", two[:]) return }
func validateTIFF(t tiff.TIFF) error { if len(t.IFDs()) == 0 { return fmt.Errorf("tiff/image: no IFDs present in tiff to process") } if t.IFDs()[0] == nil { return fmt.Errorf("tiff/image: IFD 0 is nil") } if t.IFDs()[0].NumEntries() == 0 || len(t.IFDs()[0].Fields()) == 0 { return fmt.Errorf("tiff/image: no entries found in IFD 0") } return nil }
// For now, findAlternates will only check against two concepts. One, is the // "Make" tag and the other checks the presence of tags. func findAlternateTIFFHandler(t tiff.TIFF) TIFFHandler { ifd0 := t.IFDs()[0] // Do tag presence check first. This is useful for identifying // tiff files that conform to a certain specification that uses // a tag to identify that specification. For example, tiff/ep // uses the "TIFF/EPStandardID" tag (id 37398) to indicate the // version number of tiff/ep in use for this tiff file. DNG has // the "DNGVersion" tag (id 50706) for the same. And Leaf .MOS // files do not indicate make/model, but they do have a custom // private tag that they use for certain data (tag id 34310). // The idea here is that a tiff/ep package or dng package or // leaf mos package would be able to better handle processing // the tiff than the generic package. for _, tagID := range ListRegisteredTagPresenceIDs() { if ifd0.HasField(tagID) { hndlr := GetHandlerByTagPresence(tagID) if hndlr != nil && hndlr.CanHandle(t) { return hndlr } } } // Check for a specific make next // Tag ID 271 is a baseline tag for the "Make" or maker or // manufacturer of a device that created this tiff. The idea // here is that a separate manufacturer package may be more // directly aware of how to process a specific tiff as opposed // to playing guessing games in a generic package. if ifd0.HasField(271) { f := ifd0.GetField(271) maker := string(bytes.TrimRight(f.Value().Bytes(), " \x00")) hndlr := GetHandlerByMake(maker) if hndlr != nil && hndlr.CanHandle(t) { return hndlr } } return nil }
func getDecoder(t tiff.TIFF) (dec Decoder, err error) { if err = validateTIFF(t); err != nil { return } // Look for alternates that can handle the whole tiff. if handlr := findAlternateTIFFHandler(t); handlr != nil { return handlr.Decoder(t) } // Look for alternates that can handle specific IFDs. for _, ifd := range t.IFDs() { if handlr := findAlternateIFDHandler(ifd); handlr != nil { return handlr.Decoder(ifd, t.R()) } } // If no alternates are found... do our own thing as best we can, which // means baseline support only. ifd0 := t.IFDs()[0] if !new(BaselineHandler).CanHandle(ifd0) { return nil, fmt.Errorf("tiff/image: no handlers available for this tiff") } return new(BaselineHandler).Decoder(ifd0, t.R()) }