Ignore:
Timestamp:
2015-03-10T01:17:39+01:00 (11 years ago)
Author:
Don-vip
Message:

fix #11162 - update to metadata-extractor 2.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/com/drew/metadata/exif/ExifReader.java

    r6209 r8132  
    11/*
    2  * Copyright 2002-2012 Drew Noakes
     2 * Copyright 2002-2015 Drew Noakes
    33 *
    44 *    Licensed under the Apache License, Version 2.0 (the "License");
     
    1616 * More information about this project is available at:
    1717 *
    18  *    http://drewnoakes.com/code/exif/
    19  *    http://code.google.com/p/metadata-extractor/
     18 *    https://drewnoakes.com/code/exif/
     19 *    https://github.com/drewnoakes/metadata-extractor
    2020 */
    2121package com.drew.metadata.exif;
    2222
    23 import java.util.HashSet;
    24 import java.util.Set;
     23import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
     24import com.drew.imaging.jpeg.JpegSegmentType;
     25import com.drew.imaging.tiff.TiffProcessingException;
     26import com.drew.imaging.tiff.TiffReader;
     27import com.drew.lang.ByteArrayReader;
     28import com.drew.lang.annotations.NotNull;
     29import com.drew.metadata.Metadata;
    2530
    26 import com.drew.lang.BufferBoundsException;
    27 import com.drew.lang.BufferReader;
    28 import com.drew.lang.Rational;
    29 import com.drew.lang.annotations.NotNull;
    30 import com.drew.metadata.Directory;
    31 import com.drew.metadata.Metadata;
    32 import com.drew.metadata.MetadataReader;
     31import java.io.IOException;
     32import java.util.Arrays;
    3333
    3434/**
    3535 * Decodes Exif binary data, populating a {@link Metadata} object with tag values in {@link ExifSubIFDDirectory},
    36  * {@link ExifThumbnailDirectory}, {@link ExifInteropDirectory}, {@link GpsDirectory} and one of the many camera makernote directories.
     36 * {@link ExifThumbnailDirectory}, {@link ExifInteropDirectory}, {@link GpsDirectory} and one of the many camera
     37 * makernote directories.
    3738 *
    38  * @author Drew Noakes http://drewnoakes.com
     39 * @author Drew Noakes https://drewnoakes.com
    3940 */
    40 public class ExifReader implements MetadataReader
     41public class ExifReader implements JpegSegmentMetadataReader
    4142{
    42     // TODO extract a reusable TiffReader from this class with hooks for special tag handling and subdir following
    43    
    44     /** The number of bytes used per format descriptor. */
     43    /**
     44     * The offset at which the TIFF data actually starts. This may be necessary when, for example, processing
     45     * JPEG Exif data from APP0 which has a 6-byte preamble before starting the TIFF data.
     46     */
     47    private static final String JPEG_EXIF_SEGMENT_PREAMBLE = "Exif\0\0";
     48
     49    private boolean _storeThumbnailBytes = true;
     50
     51    public boolean isStoreThumbnailBytes()
     52    {
     53        return _storeThumbnailBytes;
     54    }
     55
     56    public void setStoreThumbnailBytes(boolean storeThumbnailBytes)
     57    {
     58        _storeThumbnailBytes = storeThumbnailBytes;
     59    }
     60
    4561    @NotNull
    46     private static final int[] BYTES_PER_FORMAT = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 };
     62    public Iterable<JpegSegmentType> getSegmentTypes()
     63    {
     64        return Arrays.asList(JpegSegmentType.APP1);
     65    }
    4766
    48     /** The number of formats known. */
    49     private static final int MAX_FORMAT_CODE = 12;
     67    public boolean canProcess(@NotNull final byte[] segmentBytes, @NotNull final JpegSegmentType segmentType)
     68    {
     69        return segmentBytes.length >= JPEG_EXIF_SEGMENT_PREAMBLE.length() && new String(segmentBytes, 0, JPEG_EXIF_SEGMENT_PREAMBLE.length()).equalsIgnoreCase(JPEG_EXIF_SEGMENT_PREAMBLE);
     70    }
    5071
    51     // Format types
    52     // TODO use an enum for these?
    53     /** An 8-bit unsigned integer. */
    54     private static final int FMT_BYTE = 1;
    55     /** A fixed-length character string. */
    56     private static final int FMT_STRING = 2;
    57     /** An unsigned 16-bit integer. */
    58     private static final int FMT_USHORT = 3;
    59     /** An unsigned 32-bit integer. */
    60     private static final int FMT_ULONG = 4;
    61     private static final int FMT_URATIONAL = 5;
    62     /** An 8-bit signed integer. */
    63     private static final int FMT_SBYTE = 6;
    64     private static final int FMT_UNDEFINED = 7;
    65     /** A signed 16-bit integer. */
    66     private static final int FMT_SSHORT = 8;
    67     /** A signed 32-bit integer. */
    68     private static final int FMT_SLONG = 9;
    69     private static final int FMT_SRATIONAL = 10;
    70     /** A 32-bit floating point number. */
    71     private static final int FMT_SINGLE = 11;
    72     /** A 64-bit floating point number. */
    73     private static final int FMT_DOUBLE = 12;
     72    public void extract(@NotNull final byte[] segmentBytes, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType)
     73    {
     74        if (segmentBytes == null)
     75            throw new NullPointerException("segmentBytes cannot be null");
     76        if (metadata == null)
     77            throw new NullPointerException("metadata cannot be null");
     78        if (segmentType == null)
     79            throw new NullPointerException("segmentType cannot be null");
    7480
    75     /** This tag is a pointer to the Exif SubIFD. */
    76     public static final int TAG_EXIF_SUB_IFD_OFFSET = 0x8769;
    77     /** This tag is a pointer to the Exif Interop IFD. */
    78     public static final int TAG_INTEROP_OFFSET = 0xA005;
    79     /** This tag is a pointer to the Exif GPS IFD. */
    80     public static final int TAG_GPS_INFO_OFFSET = 0x8825;
    81     /** This tag is a pointer to the Exif Makernote IFD. */
    82     public static final int TAG_MAKER_NOTE_OFFSET = 0x927C;
     81        try {
     82            ByteArrayReader reader = new ByteArrayReader(segmentBytes);
    8383
    84     public static final int TIFF_HEADER_START_OFFSET = 6;
    85 
    86     /**
    87      * Performs the Exif data extraction, adding found values to the specified
    88      * instance of <code>Metadata</code>.
    89      *
    90      * @param reader   The buffer reader from which Exif data should be read.
    91      * @param metadata The Metadata object into which extracted values should be merged.
    92      */
    93     public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
    94     {
    95         final ExifSubIFDDirectory directory = metadata.getOrCreateDirectory(ExifSubIFDDirectory.class);
    96 
    97         // check for the header length
    98         if (reader.getLength() <= 14) {
    99             directory.addError("Exif data segment must contain at least 14 bytes");
    100             return;
    101         }
    102 
    103         // check for the header preamble
    104         try {
    105             if (!reader.getString(0, 6).equals("Exif\0\0")) {
    106                 directory.addError("Exif data segment doesn't begin with 'Exif'");
     84            //
     85            // Check for the header preamble
     86            //
     87            try {
     88                if (!reader.getString(0, JPEG_EXIF_SEGMENT_PREAMBLE.length()).equals(JPEG_EXIF_SEGMENT_PREAMBLE)) {
     89                    // TODO what do to with this error state?
     90                    System.err.println("Invalid JPEG Exif segment preamble");
     91                    return;
     92                }
     93            } catch (IOException e) {
     94                // TODO what do to with this error state?
     95                e.printStackTrace(System.err);
    10796                return;
    10897            }
    10998
    110             extractIFD(metadata, metadata.getOrCreateDirectory(ExifIFD0Directory.class), TIFF_HEADER_START_OFFSET, reader);
    111         } catch (BufferBoundsException e) {
    112             directory.addError("Exif data segment ended prematurely");
     99            //
     100            // Read the TIFF-formatted Exif data
     101            //
     102            new TiffReader().processTiff(
     103                reader,
     104                new ExifTiffHandler(metadata, _storeThumbnailBytes),
     105                JPEG_EXIF_SEGMENT_PREAMBLE.length()
     106            );
     107
     108        } catch (TiffProcessingException e) {
     109            // TODO what do to with this error state?
     110            e.printStackTrace(System.err);
     111        } catch (IOException e) {
     112            // TODO what do to with this error state?
     113            e.printStackTrace(System.err);
    113114        }
    114115    }
    115 
    116     /**
    117      * Performs the Exif data extraction on a TIFF/RAW, adding found values to the specified
    118      * instance of <code>Metadata</code>.
    119      *
    120      * @param reader   The BufferReader from which TIFF data should be read.
    121      * @param metadata The Metadata object into which extracted values should be merged.
    122      */
    123     public void extractTiff(@NotNull BufferReader reader, @NotNull Metadata metadata)
    124     {
    125         final ExifIFD0Directory directory = metadata.getOrCreateDirectory(ExifIFD0Directory.class);
    126 
    127         try {
    128             extractIFD(metadata, directory, 0, reader);
    129         } catch (BufferBoundsException e) {
    130             directory.addError("Exif data segment ended prematurely");
    131         }
    132     }
    133 
    134     private void extractIFD(@NotNull Metadata metadata, @NotNull final ExifIFD0Directory directory, int tiffHeaderOffset, @NotNull BufferReader reader) throws BufferBoundsException
    135     {
    136         // this should be either "MM" or "II"
    137         String byteOrderIdentifier = reader.getString(tiffHeaderOffset, 2);
    138 
    139         if ("MM".equals(byteOrderIdentifier)) {
    140             reader.setMotorolaByteOrder(true);
    141         } else if ("II".equals(byteOrderIdentifier)) {
    142             reader.setMotorolaByteOrder(false);
    143         } else {
    144             directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
    145             return;
    146         }
    147 
    148         // Check the next two values for correctness.
    149         final int tiffMarker = reader.getUInt16(2 + tiffHeaderOffset);
    150 
    151         final int standardTiffMarker = 0x002A;
    152         final int olympusRawTiffMarker = 0x4F52; // for ORF files
    153         final int panasonicRawTiffMarker = 0x0055; // for RW2 files
    154 
    155         if (tiffMarker != standardTiffMarker && tiffMarker != olympusRawTiffMarker && tiffMarker != panasonicRawTiffMarker) {
    156             directory.addError("Unexpected TIFF marker after byte order identifier: 0x" + Integer.toHexString(tiffMarker));
    157             return;
    158         }
    159 
    160         int firstDirectoryOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset;
    161 
    162         // David Ekholm sent a digital camera image that has this problem
    163         if (firstDirectoryOffset >= reader.getLength() - 1) {
    164             directory.addError("First exif directory offset is beyond end of Exif data segment");
    165             // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
    166             firstDirectoryOffset = 14;
    167         }
    168 
    169         Set<Integer> processedDirectoryOffsets = new HashSet<Integer>();
    170 
    171         processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, tiffHeaderOffset, metadata, reader);
    172 
    173         // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
    174         ExifThumbnailDirectory thumbnailDirectory = metadata.getDirectory(ExifThumbnailDirectory.class);
    175         if (thumbnailDirectory!=null && thumbnailDirectory.containsTag(ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION)) {
    176             Integer offset = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET);
    177             Integer length = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH);
    178             if (offset != null && length != null) {
    179                 try {
    180                     byte[] thumbnailData = reader.getBytes(tiffHeaderOffset + offset, length);
    181                     thumbnailDirectory.setThumbnailData(thumbnailData);
    182                 } catch (BufferBoundsException ex) {
    183                     directory.addError("Invalid thumbnail data specification: " + ex.getMessage());
    184                 }
    185             }
    186         }
    187     }
    188 
    189     /**
    190      * Process one of the nested Tiff IFD directories.
    191      * <p/>
    192      * Header
    193      * 2 bytes: number of tags
    194      * <p/>
    195      * Then for each tag
    196      * 2 bytes: tag type
    197      * 2 bytes: format code
    198      * 4 bytes: component count
    199      */
    200     private void processDirectory(@NotNull Directory directory, @NotNull Set<Integer> processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull final BufferReader reader) throws BufferBoundsException
    201     {
    202         // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
    203         if (processedDirectoryOffsets.contains(Integer.valueOf(dirStartOffset)))
    204             return;
    205 
    206         // remember that we've visited this directory so that we don't visit it again later
    207         processedDirectoryOffsets.add(dirStartOffset);
    208 
    209         if (dirStartOffset >= reader.getLength() || dirStartOffset < 0) {
    210             directory.addError("Ignored directory marked to start outside data segment");
    211             return;
    212         }
    213 
    214         // First two bytes in the IFD are the number of tags in this directory
    215         int dirTagCount = reader.getUInt16(dirStartOffset);
    216 
    217         int dirLength = (2 + (12 * dirTagCount) + 4);
    218         if (dirLength + dirStartOffset > reader.getLength()) {
    219             directory.addError("Illegally sized directory");
    220             return;
    221         }
    222 
    223         // Handle each tag in this directory
    224         for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) {
    225             final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber);
    226 
    227             // 2 bytes for the tag type
    228             final int tagType = reader.getUInt16(tagOffset);
    229 
    230             // 2 bytes for the format code
    231             final int formatCode = reader.getUInt16(tagOffset + 2);
    232             if (formatCode < 1 || formatCode > MAX_FORMAT_CODE) {
    233                 // This error suggests that we are processing at an incorrect index and will generate
    234                 // rubbish until we go out of bounds (which may be a while).  Exit now.
    235                 directory.addError("Invalid TIFF tag format code: " + formatCode);
    236                 continue; // JOSM patch to fix #9030
    237             }
    238 
    239             // 4 bytes dictate the number of components in this tag's data
    240             final int componentCount = reader.getInt32(tagOffset + 4);
    241             if (componentCount < 0) {
    242                 directory.addError("Negative TIFF tag component count");
    243                 continue;
    244             }
    245             // each component may have more than one byte... calculate the total number of bytes
    246             final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode];
    247             final int tagValueOffset;
    248             if (byteCount > 4) {
    249                 // If it's bigger than 4 bytes, the dir entry contains an offset.
    250                 // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an
    251                 // offset relative to the start of the makernote itself, not the TIFF segment.
    252                 final int offsetVal = reader.getInt32(tagOffset + 8);
    253                 if (offsetVal + byteCount > reader.getLength()) {
    254                     // Bogus pointer offset and / or byteCount value
    255                     directory.addError("Illegal TIFF tag pointer offset");
    256                     continue;
    257                 }
    258                 tagValueOffset = tiffHeaderOffset + offsetVal;
    259             } else {
    260                 // 4 bytes or less and value is in the dir entry itself
    261                 tagValueOffset = tagOffset + 8;
    262             }
    263 
    264             if (tagValueOffset < 0 || tagValueOffset > reader.getLength()) {
    265                 directory.addError("Illegal TIFF tag pointer offset");
    266                 continue;
    267             }
    268 
    269             // Check that this tag isn't going to allocate outside the bounds of the data array.
    270             // This addresses an uncommon OutOfMemoryError.
    271             if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) {
    272                 directory.addError("Illegal number of bytes: " + byteCount);
    273                 continue;
    274             }
    275 
    276             switch (tagType) {
    277                 case TAG_EXIF_SUB_IFD_OFFSET: {
    278                     final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset);
    279                     processDirectory(metadata.getOrCreateDirectory(ExifSubIFDDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    280                     continue;
    281                 }
    282                 case TAG_INTEROP_OFFSET: {
    283                     final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset);
    284                     processDirectory(metadata.getOrCreateDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    285                     continue;
    286                 }
    287                 case TAG_GPS_INFO_OFFSET: {
    288                     final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset);
    289                     processDirectory(metadata.getOrCreateDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    290                     continue;
    291                 }
    292                 case TAG_MAKER_NOTE_OFFSET: {
    293                     processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset, metadata, reader);
    294                     continue;
    295                 }
    296                 default: {
    297                     processTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader);
    298                     break;
    299                 }
    300             }
    301         }
    302 
    303         // at the end of each IFD is an optional link to the next IFD
    304         final int finalTagOffset = calculateTagOffset(dirStartOffset, dirTagCount);
    305         int nextDirectoryOffset = reader.getInt32(finalTagOffset);
    306         if (nextDirectoryOffset != 0) {
    307             nextDirectoryOffset += tiffHeaderOffset;
    308             if (nextDirectoryOffset >= reader.getLength()) {
    309                 // Last 4 bytes of IFD reference another IFD with an address that is out of bounds
    310                 // Note this could have been caused by jhead 1.3 cropping too much
    311                 return;
    312             } else if (nextDirectoryOffset < dirStartOffset) {
    313                 // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
    314                 return;
    315             }
    316             // TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case
    317             final ExifThumbnailDirectory nextDirectory = metadata.getOrCreateDirectory(ExifThumbnailDirectory.class);
    318             processDirectory(nextDirectory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader);
    319         }
    320     }
    321 
    322     private void processMakerNote(int subdirOffset, @NotNull Set<Integer> processedDirectoryOffsets, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull BufferReader reader) throws BufferBoundsException
    323     {
    324         // Determine the camera model and makernote format
    325         Directory ifd0Directory = metadata.getDirectory(ExifIFD0Directory.class);
    326 
    327         if (ifd0Directory==null)
    328             return;
    329 
    330         String cameraModel = ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE);
    331 
    332         //final String firstTwoChars = reader.getString(subdirOffset, 2);
    333         final String firstThreeChars = reader.getString(subdirOffset, 3);
    334         final String firstFourChars = reader.getString(subdirOffset, 4);
    335         final String firstFiveChars = reader.getString(subdirOffset, 5);
    336         final String firstSixChars = reader.getString(subdirOffset, 6);
    337         final String firstSevenChars = reader.getString(subdirOffset, 7);
    338         final String firstEightChars = reader.getString(subdirOffset, 8);
    339         final String firstTwelveChars = reader.getString(subdirOffset, 12);
    340 
    341         if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) {
    342             // Olympus Makernote
    343             // Epson and Agfa use Olympus maker note standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/
    344             processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader);
    345         } else if (cameraModel != null && cameraModel.trim().toUpperCase().startsWith("NIKON")) {
    346             if ("Nikon".equals(firstFiveChars)) {
    347                 /* There are two scenarios here:
    348                  * Type 1:                  **
    349                  * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
    350                  * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
    351                  * Type 3:                  **
    352                  * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
    353                  * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
    354                  */
    355                 switch (reader.getUInt8(subdirOffset + 6)) {
    356                     case 1:
    357                         processDirectory(metadata.getOrCreateDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader);
    358                         break;
    359                     case 2:
    360                         processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10, metadata, reader);
    361                         break;
    362                     default:
    363                         ifd0Directory.addError("Unsupported Nikon makernote data ignored.");
    364                         break;
    365                 }
    366             } else {
    367                 // The IFD begins with the first MakerNote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
    368                 processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    369             }
    370         } else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars)) {
    371             processDirectory(metadata.getOrCreateDirectory(SonyType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader);
    372         } else if ("SIGMA\u0000\u0000\u0000".equals(firstEightChars) || "FOVEON\u0000\u0000".equals(firstEightChars)) {
    373             processDirectory(metadata.getOrCreateDirectory(SigmaMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 10, tiffHeaderOffset, metadata, reader);
    374         } else if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".equals(firstTwelveChars)) {
    375             // force MM for this directory
    376             boolean isMotorola = reader.isMotorolaByteOrder();
    377             reader.setMotorolaByteOrder(true);
    378             // skip 12 byte header + 2 for "MM" + 6
    379             processDirectory(metadata.getOrCreateDirectory(SonyType6MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader);
    380             reader.setMotorolaByteOrder(isMotorola);
    381         } else if ("KDK".equals(firstThreeChars)) {
    382             processDirectory(metadata.getOrCreateDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader);
    383         } else if ("Canon".equalsIgnoreCase(cameraModel)) {
    384             processDirectory(metadata.getOrCreateDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    385         } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("CASIO")) {
    386             if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
    387                 processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset, metadata, reader);
    388             else
    389                 processDirectory(metadata.getOrCreateDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    390         } else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel)) {
    391             boolean byteOrderBefore = reader.isMotorolaByteOrder();
    392             // bug in fujifilm makernote ifd means we temporarily use Intel byte ordering
    393             reader.setMotorolaByteOrder(false);
    394             // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
    395             // IFD, though the offset is relative to the start of the makernote, not the TIFF
    396             // header (like everywhere else)
    397             int ifdStart = subdirOffset + reader.getInt32(subdirOffset + 8);
    398             processDirectory(metadata.getOrCreateDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset, metadata, reader);
    399             reader.setMotorolaByteOrder(byteOrderBefore);
    400         } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("MINOLTA")) {
    401             // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
    402             // area that commences immediately.
    403             processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    404         } else if ("KYOCERA".equals(firstSevenChars)) {
    405             // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
    406             processDirectory(metadata.getOrCreateDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset, metadata, reader);
    407         } else if ("Panasonic\u0000\u0000\u0000".equals(reader.getString(subdirOffset, 12))) {
    408             // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
    409             // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
    410             // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
    411             processDirectory(metadata.getOrCreateDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader);
    412         } else if ("AOC\u0000".equals(firstFourChars)) {
    413             // NON-Standard TIFF IFD Data using Casio Type 2 Tags
    414             // IFD has no Next-IFD pointer at end of IFD, and
    415             // Offsets are relative to the start of the current IFD tag, not the TIFF header
    416             // Observed for:
    417             // - Pentax ist D
    418             processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, subdirOffset, metadata, reader);
    419         } else if (cameraModel != null && (cameraModel.toUpperCase().startsWith("PENTAX") || cameraModel.toUpperCase().startsWith("ASAHI"))) {
    420             // NON-Standard TIFF IFD Data using Pentax Tags
    421             // IFD has no Next-IFD pointer at end of IFD, and
    422             // Offsets are relative to the start of the current IFD tag, not the TIFF header
    423             // Observed for:
    424             // - PENTAX Optio 330
    425             // - PENTAX Optio 430
    426             processDirectory(metadata.getOrCreateDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset, metadata, reader);
    427 //        } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) {
    428 //            // This Konica data is not understood.  Header identified in accordance with information at this site:
    429 //            // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html
    430 //            // TODO add support for minolta/konica cameras
    431 //            exifDirectory.addError("Unsupported Konica/Minolta data ignored.");
    432         } else {
    433             // TODO how to store makernote data when it's not from a supported camera model?
    434             // this is difficult as the starting offset is not known.  we could look for it...
    435         }
    436     }
    437 
    438     private void processTag(@NotNull Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, @NotNull final BufferReader reader) throws BufferBoundsException
    439     {
    440         // Directory simply stores raw values
    441         // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions
    442         switch (formatCode) {
    443             case FMT_UNDEFINED:
    444                 // this includes exif user comments
    445                 directory.setByteArray(tagType, reader.getBytes(tagValueOffset, componentCount));
    446                 break;
    447             case FMT_STRING:
    448                 String string = reader.getNullTerminatedString(tagValueOffset, componentCount);
    449                 directory.setString(tagType, string);
    450 /*
    451                 // special handling for certain known tags, proposed by Yuri Binev but left out for now,
    452                 // as it gives the false impression that the image was captured in the same timezone
    453                 // in which the string is parsed
    454                 if (tagType==ExifSubIFDDirectory.TAG_DATETIME ||
    455                     tagType==ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL ||
    456                     tagType==ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED) {
    457                     String[] datePatterns = {
    458                         "yyyy:MM:dd HH:mm:ss",
    459                         "yyyy:MM:dd HH:mm",
    460                         "yyyy-MM-dd HH:mm:ss",
    461                         "yyyy-MM-dd HH:mm"};
    462                     for (String datePattern : datePatterns) {
    463                         try {
    464                             DateFormat parser = new SimpleDateFormat(datePattern);
    465                             Date date = parser.parse(string);
    466                             directory.setDate(tagType, date);
    467                             break;
    468                         } catch (ParseException ex) {
    469                             // simply try the next pattern
    470                         }
    471                     }
    472                 }
    473 */
    474                 break;
    475             case FMT_SRATIONAL:
    476                 if (componentCount == 1) {
    477                     directory.setRational(tagType, new Rational(reader.getInt32(tagValueOffset), reader.getInt32(tagValueOffset + 4)));
    478                 } else if (componentCount > 1) {
    479                     Rational[] rationals = new Rational[componentCount];
    480                     for (int i = 0; i < componentCount; i++)
    481                         rationals[i] = new Rational(reader.getInt32(tagValueOffset + (8 * i)), reader.getInt32(tagValueOffset + 4 + (8 * i)));
    482                     directory.setRationalArray(tagType, rationals);
    483                 }
    484                 break;
    485             case FMT_URATIONAL:
    486                 if (componentCount == 1) {
    487                     directory.setRational(tagType, new Rational(reader.getUInt32(tagValueOffset), reader.getUInt32(tagValueOffset + 4)));
    488                 } else if (componentCount > 1) {
    489                     Rational[] rationals = new Rational[componentCount];
    490                     for (int i = 0; i < componentCount; i++)
    491                         rationals[i] = new Rational(reader.getUInt32(tagValueOffset + (8 * i)), reader.getUInt32(tagValueOffset + 4 + (8 * i)));
    492                     directory.setRationalArray(tagType, rationals);
    493                 }
    494                 break;
    495             case FMT_SINGLE:
    496                 if (componentCount == 1) {
    497                     directory.setFloat(tagType, reader.getFloat32(tagValueOffset));
    498                 } else {
    499                     float[] floats = new float[componentCount];
    500                     for (int i = 0; i < componentCount; i++)
    501                         floats[i] = reader.getFloat32(tagValueOffset + (i * 4));
    502                     directory.setFloatArray(tagType, floats);
    503                 }
    504                 break;
    505             case FMT_DOUBLE:
    506                 if (componentCount == 1) {
    507                     directory.setDouble(tagType, reader.getDouble64(tagValueOffset));
    508                 } else {
    509                     double[] doubles = new double[componentCount];
    510                     for (int i = 0; i < componentCount; i++)
    511                         doubles[i] = reader.getDouble64(tagValueOffset + (i * 4));
    512                     directory.setDoubleArray(tagType, doubles);
    513                 }
    514                 break;
    515 
    516             //
    517             // Note that all integral types are stored as int32 internally (the largest supported by TIFF)
    518             //
    519 
    520             case FMT_SBYTE:
    521                 if (componentCount == 1) {
    522                     directory.setInt(tagType, reader.getInt8(tagValueOffset));
    523                 } else {
    524                     int[] bytes = new int[componentCount];
    525                     for (int i = 0; i < componentCount; i++)
    526                         bytes[i] = reader.getInt8(tagValueOffset + i);
    527                     directory.setIntArray(tagType, bytes);
    528                 }
    529                 break;
    530             case FMT_BYTE:
    531                 if (componentCount == 1) {
    532                     directory.setInt(tagType, reader.getUInt8(tagValueOffset));
    533                 } else {
    534                     int[] bytes = new int[componentCount];
    535                     for (int i = 0; i < componentCount; i++)
    536                         bytes[i] = reader.getUInt8(tagValueOffset + i);
    537                     directory.setIntArray(tagType, bytes);
    538                 }
    539                 break;
    540             case FMT_USHORT:
    541                 if (componentCount == 1) {
    542                     int i = reader.getUInt16(tagValueOffset);
    543                     directory.setInt(tagType, i);
    544                 } else {
    545                     int[] ints = new int[componentCount];
    546                     for (int i = 0; i < componentCount; i++)
    547                         ints[i] = reader.getUInt16(tagValueOffset + (i * 2));
    548                     directory.setIntArray(tagType, ints);
    549                 }
    550                 break;
    551             case FMT_SSHORT:
    552                 if (componentCount == 1) {
    553                     int i = reader.getInt16(tagValueOffset);
    554                     directory.setInt(tagType, i);
    555                 } else {
    556                     int[] ints = new int[componentCount];
    557                     for (int i = 0; i < componentCount; i++)
    558                         ints[i] = reader.getInt16(tagValueOffset + (i * 2));
    559                     directory.setIntArray(tagType, ints);
    560                 }
    561                 break;
    562             case FMT_SLONG:
    563             case FMT_ULONG:
    564                 // NOTE 'long' in this case means 32 bit, not 64
    565                 if (componentCount == 1) {
    566                     int i = reader.getInt32(tagValueOffset);
    567                     directory.setInt(tagType, i);
    568                 } else {
    569                     int[] ints = new int[componentCount];
    570                     for (int i = 0; i < componentCount; i++)
    571                         ints[i] = reader.getInt32(tagValueOffset + (i * 4));
    572                     directory.setIntArray(tagType, ints);
    573                 }
    574                 break;
    575             default:
    576                 directory.addError("Unknown format code " + formatCode + " for tag " + tagType);
    577         }
    578     }
    579 
    580     /**
    581      * Determine the offset at which a given InteropArray entry begins within the specified IFD.
    582      *
    583      * @param dirStartOffset the offset at which the IFD starts
    584      * @param entryNumber    the zero-based entry number
    585      */
    586     private int calculateTagOffset(int dirStartOffset, int entryNumber)
    587     {
    588         // add 2 bytes for the tag count
    589         // each entry is 12 bytes, so we skip 12 * the number seen so far
    590         return dirStartOffset + 2 + (12 * entryNumber);
    591     }
    592116}
Note: See TracChangeset for help on using the changeset viewer.