Changeset 6127 in josm for trunk/src/com/drew/metadata/exif


Ignore:
Timestamp:
2013-08-09T18:05:11+02:00 (8 years ago)
Author:
bastiK
Message:

applied #8895 - Upgrade metadata-extractor to v. 2.6.4 (patch by ebourg)

Location:
trunk/src/com/drew/metadata/exif
Files:
12 added
5 deleted
28 edited

Legend:

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

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
     28 * Provides human-readable string representations of tag values stored in a <code>CanonMakernoteDirectory</code>.
    2429 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class CanonMakernoteDescriptor extends TagDescriptor
     32public class CanonMakernoteDescriptor extends TagDescriptor<CanonMakernoteDirectory>
    2733{
    28     public CanonMakernoteDescriptor(Directory directory)
     34    public CanonMakernoteDescriptor(@NotNull CanonMakernoteDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
    36             case CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY:
     43            case CanonMakernoteDirectory.TAG_CANON_SERIAL_NUMBER:
     44                return getSerialNumberDescription();
     45            case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_ACTIVITY:
    3746                return getFlashActivityDescription();
    38             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE:
     47            case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE:
    3948                return getFocusTypeDescription();
    40             case CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM:
     49            case CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM:
    4150                return getDigitalZoomDescription();
    42             case CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY:
     51            case CanonMakernoteDirectory.CameraSettings.TAG_QUALITY:
    4352                return getQualityDescription();
    44             case CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE:
     53            case CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE:
    4554                return getMacroModeDescription();
    46             case CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY:
     55            case CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY:
    4756                return getSelfTimerDelayDescription();
    48             case CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE:
     57            case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE:
    4958                return getFlashModeDescription();
    50             case CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE:
     59            case CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE:
    5160                return getContinuousDriveModeDescription();
    52             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1:
     61            case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1:
    5362                return getFocusMode1Description();
    54             case CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE:
     63            case CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE:
    5564                return getImageSizeDescription();
    56             case CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE:
     65            case CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE:
    5766                return getEasyShootingModeDescription();
    58             case CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST:
     67            case CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST:
    5968                return getContrastDescription();
    60             case CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION:
     69            case CanonMakernoteDirectory.CameraSettings.TAG_SATURATION:
    6170                return getSaturationDescription();
    62             case CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS:
     71            case CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS:
    6372                return getSharpnessDescription();
    64             case CanonMakernoteDirectory.TAG_CANON_STATE1_ISO:
     73            case CanonMakernoteDirectory.CameraSettings.TAG_ISO:
    6574                return getIsoDescription();
    66             case CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE:
     75            case CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE:
    6776                return getMeteringModeDescription();
    68             case CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED:
     77            case CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED:
    6978                return getAfPointSelectedDescription();
    70             case CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE:
     79            case CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE:
    7180                return getExposureModeDescription();
    72             case CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH:
     81            case CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH:
    7382                return getLongFocalLengthDescription();
    74             case CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH:
     83            case CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH:
    7584                return getShortFocalLengthDescription();
    76             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM:
     85            case CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM:
    7786                return getFocalUnitsPerMillimetreDescription();
    78             case CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS:
     87            case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS:
    7988                return getFlashDetailsDescription();
    80             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2:
     89            case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2:
    8190                return getFocusMode2Description();
    82             case CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE:
     91            case CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE:
    8392                return getWhiteBalanceDescription();
    84             case CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED:
     93            case CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED:
    8594                return getAfPointUsedDescription();
    86             case CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS:
     95            case CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS:
    8796                return getFlashBiasDescription();
    88             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION:
    89                 return getLongExposureNoiseReductionDescription();
    90             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS:
    91                 return getShutterAutoExposureLockButtonDescription();
    92             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP:
    93                 return getMirrorLockupDescription();
    94             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL:
    95                 return getTvAndAvExposureLevelDescription();
    96             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT:
    97                 return getAutoFocusAssistLightDescription();
    98             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE:
    99                 return getShutterSpeedInAvModeDescription();
    100             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING:
    101                 return getAutoExposureBrackettingSequenceAndAutoCancellationDescription();
    102             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC:
    103                 return getShutterCurtainSyncDescription();
    104             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP:
    105                 return getLensAutoFocusStopButtonDescription();
    106             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION:
    107                 return getFillFlashReductionDescription();
    108             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN:
    109                 return getMenuButtonReturnPositionDescription();
    110             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION:
    111                 return getSetButtonFunctionWhenShootingDescription();
    112             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING:
    113                 return getSensorCleaningDescription();
    114             default:
    115                 return _directory.getString(tagType);
    116         }
    117     }
    118 
    119     public String getLongExposureNoiseReductionDescription() throws MetadataException
    120     {
    121         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION)) return null;
    122         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
     97
     98            // It turns out that these values are dependent upon the camera model and therefore the below code was
     99            // incorrect for some Canon models.  This needs to be revisited.
     100
     101//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION:
     102//                return getLongExposureNoiseReductionDescription();
     103//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS:
     104//                return getShutterAutoExposureLockButtonDescription();
     105//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP:
     106//                return getMirrorLockupDescription();
     107//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL:
     108//                return getTvAndAvExposureLevelDescription();
     109//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT:
     110//                return getAutoFocusAssistLightDescription();
     111//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE:
     112//                return getShutterSpeedInAvModeDescription();
     113//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING:
     114//                return getAutoExposureBrackettingSequenceAndAutoCancellationDescription();
     115//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC:
     116//                return getShutterCurtainSyncDescription();
     117//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP:
     118//                return getLensAutoFocusStopButtonDescription();
     119//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION:
     120//                return getFillFlashReductionDescription();
     121//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN:
     122//                return getMenuButtonReturnPositionDescription();
     123//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION:
     124//                return getSetButtonFunctionWhenShootingDescription();
     125//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING:
     126//                return getSensorCleaningDescription();
     127            default:
     128                return super.getDescription(tagType);
     129        }
     130    }
     131
     132    @Nullable
     133    public String getSerialNumberDescription()
     134    {
     135        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_SERIAL_NUMBER);
     136        if (value==null)
     137            return null;
     138        return String.format("%04X%05d", (value >> 8) & 0xFF, value & 0xFF);
     139    }
     140
     141/*
     142    @Nullable
     143    public String getLongExposureNoiseReductionDescription()
     144    {
     145        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
     146        if (value==null)
     147            return null;
    123148        switch (value) {
    124149            case 0:     return "Off";
     
    127152        }
    128153    }
    129     public String getShutterAutoExposureLockButtonDescription() throws MetadataException
    130     {
    131         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS)) return null;
    132         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
     154
     155    @Nullable
     156    public String getShutterAutoExposureLockButtonDescription()
     157    {
     158        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
     159        if (value==null)
     160            return null;
    133161        switch (value) {
    134162            case 0:     return "AF/AE lock";
     
    139167        }
    140168    }
    141     public String getMirrorLockupDescription() throws MetadataException
    142     {
    143         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP)) return null;
    144         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
     169
     170    @Nullable
     171    public String getMirrorLockupDescription()
     172    {
     173        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
     174        if (value==null)
     175            return null;
    145176        switch (value) {
    146177            case 0:     return "Disabled";
     
    149180        }
    150181    }
    151     public String getTvAndAvExposureLevelDescription() throws MetadataException
    152     {
    153         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL)) return null;
    154         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
     182
     183    @Nullable
     184    public String getTvAndAvExposureLevelDescription()
     185    {
     186        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
     187        if (value==null)
     188            return null;
    155189        switch (value) {
    156190            case 0:     return "1/2 stop";
     
    159193        }
    160194    }
    161     public String getAutoFocusAssistLightDescription() throws MetadataException
    162     {
    163         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT)) return null;
    164         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
     195
     196    @Nullable
     197    public String getAutoFocusAssistLightDescription()
     198    {
     199        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
     200        if (value==null)
     201            return null;
    165202        switch (value) {
    166203            case 0:     return "On (Auto)";
     
    169206        }
    170207    }
    171     public String getShutterSpeedInAvModeDescription() throws MetadataException
    172     {
    173         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE)) return null;
    174         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
     208
     209    @Nullable
     210    public String getShutterSpeedInAvModeDescription()
     211    {
     212        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
     213        if (value==null)
     214            return null;
    175215        switch (value) {
    176216            case 0:     return "Automatic";
     
    179219        }
    180220    }
    181     public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() throws MetadataException
    182     {
    183         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING)) return null;
    184         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING);
     221
     222    @Nullable
     223    public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription()
     224    {
     225        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING);
     226        if (value==null)
     227            return null;
    185228        switch (value) {
    186229            case 0:     return "0,-,+ / Enabled";
     
    191234        }
    192235    }
    193     public String getShutterCurtainSyncDescription() throws MetadataException
    194     {
    195         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC)) return null;
    196         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
     236
     237    @Nullable
     238    public String getShutterCurtainSyncDescription()
     239    {
     240        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
     241        if (value==null)
     242            return null;
    197243        switch (value) {
    198244            case 0:     return "1st Curtain Sync";
     
    201247        }
    202248    }
    203     public String getLensAutoFocusStopButtonDescription() throws MetadataException
    204     {
    205         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP)) return null;
    206         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
     249
     250    @Nullable
     251    public String getLensAutoFocusStopButtonDescription()
     252    {
     253        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
     254        if (value==null)
     255            return null;
    207256        switch (value) {
    208257            case 0:     return "AF stop";
     
    212261        }
    213262    }
    214     public String getFillFlashReductionDescription() throws MetadataException
    215     {
    216         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION)) return null;
    217         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
     263
     264    @Nullable
     265    public String getFillFlashReductionDescription()
     266    {
     267        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
     268        if (value==null)
     269            return null;
    218270        switch (value) {
    219271            case 0:     return "Enabled";
     
    222274        }
    223275    }
    224     public String getMenuButtonReturnPositionDescription() throws MetadataException
    225     {
    226         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN)) return null;
    227         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
     276
     277    @Nullable
     278    public String getMenuButtonReturnPositionDescription()
     279    {
     280        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
     281        if (value==null)
     282            return null;
    228283        switch (value) {
    229284            case 0:     return "Top";
     
    233288        }
    234289    }
    235     public String getSetButtonFunctionWhenShootingDescription() throws MetadataException
    236     {
    237         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION)) return null;
    238         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
     290
     291    @Nullable
     292    public String getSetButtonFunctionWhenShootingDescription()
     293    {
     294        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
     295        if (value==null)
     296            return null;
    239297        switch (value) {
    240298            case 0:     return "Not Assigned";
     
    245303        }
    246304    }
    247     public String getSensorCleaningDescription() throws MetadataException
    248     {
    249         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING)) return null;
    250         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
     305
     306    @Nullable
     307    public String getSensorCleaningDescription()
     308    {
     309        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
     310        if (value==null)
     311            return null;
    251312        switch (value) {
    252313            case 0:     return "Disabled";
     
    255316        }
    256317    }
    257 
    258     public String getFlashBiasDescription() throws MetadataException
    259     {
    260         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS)) return null;
    261 
    262         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS);
     318*/
     319
     320    @Nullable
     321    public String getFlashBiasDescription()
     322    {
     323        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS);
     324
     325        if (value==null)
     326            return null;
    263327
    264328        boolean isNegative = false;
     
    278342    }
    279343
    280     public String getAfPointUsedDescription() throws MetadataException
    281     {
    282         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED)) return null;
    283         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED);
     344    @Nullable
     345    public String getAfPointUsedDescription()
     346    {
     347        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED);
     348        if (value==null)
     349            return null;
    284350        if ((value & 0x7) == 0) {
    285351            return "Right";
     
    293359    }
    294360
    295     public String getWhiteBalanceDescription() throws MetadataException
    296     {
    297         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE)) return null;
    298         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE);
     361    @Nullable
     362    public String getWhiteBalanceDescription()
     363    {
     364        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE);
     365        if (value==null)
     366            return null;
    299367        switch (value) {
    300368            case 0:
     
    307375                return "Tungsten";
    308376            case 4:
    309                 return "Flourescent";
     377                return "Florescent";
    310378            case 5:
    311379                return "Flash";
     
    317385    }
    318386
    319     public String getFocusMode2Description() throws MetadataException
    320     {
    321         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2)) return null;
    322         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2);
     387    @Nullable
     388    public String getFocusMode2Description()
     389    {
     390        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2);
     391        if (value==null)
     392            return null;
    323393        switch (value) {
    324394            case 0:
     
    331401    }
    332402
    333     public String getFlashDetailsDescription() throws MetadataException
    334     {
    335         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS)) return null;
    336         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS);
    337         if (((value << 14) & 1) > 0) {
     403    @Nullable
     404    public String getFlashDetailsDescription()
     405    {
     406        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS);
     407        if (value==null)
     408            return null;
     409        if (((value >> 14) & 1) > 0) {
    338410            return "External E-TTL";
    339411        }
    340         if (((value << 13) & 1) > 0) {
     412        if (((value >> 13) & 1) > 0) {
    341413            return "Internal flash";
    342414        }
    343         if (((value << 11) & 1) > 0) {
     415        if (((value >> 11) & 1) > 0) {
    344416            return "FP sync used";
    345417        }
    346         if (((value << 4) & 1) > 0) {
     418        if (((value >> 4) & 1) > 0) {
    347419            return "FP sync enabled";
    348420        }
     
    350422    }
    351423
    352     public String getFocalUnitsPerMillimetreDescription() throws MetadataException
    353     {
    354         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM)) return "";
    355         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM);
     424    @Nullable
     425    public String getFocalUnitsPerMillimetreDescription()
     426    {
     427        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM);
     428        if (value==null)
     429            return null;
    356430        if (value != 0) {
    357431            return Integer.toString(value);
     
    361435    }
    362436
    363     public String getShortFocalLengthDescription() throws MetadataException
    364     {
    365         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH)) return null;
    366         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH);
     437    @Nullable
     438    public String getShortFocalLengthDescription()
     439    {
     440        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH);
     441        if (value==null)
     442            return null;
    367443        String units = getFocalUnitsPerMillimetreDescription();
    368444        return Integer.toString(value) + " " + units;
    369445    }
    370446
    371     public String getLongFocalLengthDescription() throws MetadataException
    372     {
    373         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH)) return null;
    374         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH);
     447    @Nullable
     448    public String getLongFocalLengthDescription()
     449    {
     450        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH);
     451        if (value==null)
     452            return null;
    375453        String units = getFocalUnitsPerMillimetreDescription();
    376454        return Integer.toString(value) + " " + units;
    377455    }
    378456
    379     public String getExposureModeDescription() throws MetadataException
    380     {
    381         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE)) return null;
    382         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE);
     457    @Nullable
     458    public String getExposureModeDescription()
     459    {
     460        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE);
     461        if (value==null)
     462            return null;
    383463        switch (value) {
    384464            case 0:
     
    399479    }
    400480
    401     public String getAfPointSelectedDescription() throws MetadataException
    402     {
    403         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED)) return null;
    404         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED);
     481    @Nullable
     482    public String getAfPointSelectedDescription()
     483    {
     484        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED);
     485        if (value==null)
     486            return null;
    405487        switch (value) {
    406488            case 0x3000:
     
    419501    }
    420502
    421     public String getMeteringModeDescription() throws MetadataException
    422     {
    423         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE)) return null;
    424         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE);
     503    @Nullable
     504    public String getMeteringModeDescription()
     505    {
     506        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE);
     507        if (value==null)
     508            return null;
    425509        switch (value) {
    426510            case 3:
     
    435519    }
    436520
    437     public String getIsoDescription() throws MetadataException
    438     {
    439         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO)) return null;
    440         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO);
     521    @Nullable
     522    public String getIsoDescription()
     523    {
     524        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_ISO);
     525        if (value==null)
     526            return null;
     527
     528        // Canon PowerShot S3 is special
     529        int canonMask = 0x4000;
     530        if ((value & canonMask) > 0)
     531            return "" + (value & ~canonMask);
     532
    441533        switch (value) {
    442534            case 0:
     
    457549    }
    458550
    459     public String getSharpnessDescription() throws MetadataException
    460     {
    461         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS)) return null;
    462         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS);
     551    @Nullable
     552    public String getSharpnessDescription()
     553    {
     554        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS);
     555        if (value==null)
     556            return null;
    463557        switch (value) {
    464558            case 0xFFFF:
     
    473567    }
    474568
    475     public String getSaturationDescription() throws MetadataException
    476     {
    477         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION)) return null;
    478         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION);
     569    @Nullable
     570    public String getSaturationDescription()
     571    {
     572        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SATURATION);
     573        if (value==null)
     574            return null;
    479575        switch (value) {
    480576            case 0xFFFF:
     
    489585    }
    490586
    491     public String getContrastDescription() throws MetadataException
    492     {
    493         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST)) return null;
    494         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST);
     587    @Nullable
     588    public String getContrastDescription()
     589    {
     590        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST);
     591        if (value==null)
     592            return null;
    495593        switch (value) {
    496594            case 0xFFFF:
     
    505603    }
    506604
    507     public String getEasyShootingModeDescription() throws MetadataException
    508     {
    509         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE)) return null;
    510         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE);
     605    @Nullable
     606    public String getEasyShootingModeDescription()
     607    {
     608        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE);
     609        if (value==null)
     610            return null;
    511611        switch (value) {
    512612            case 0:
     
    539639    }
    540640
    541     public String getImageSizeDescription() throws MetadataException
    542     {
    543         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE)) return null;
    544         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE);
     641    @Nullable
     642    public String getImageSizeDescription()
     643    {
     644        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE);
     645        if (value==null)
     646            return null;
    545647        switch (value) {
    546648            case 0:
     
    555657    }
    556658
    557     public String getFocusMode1Description() throws MetadataException
    558     {
    559         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1)) return null;
    560         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1);
     659    @Nullable
     660    public String getFocusMode1Description()
     661    {
     662        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1);
     663        if (value==null)
     664            return null;
    561665        switch (value) {
    562666            case 0:
     
    580684    }
    581685
    582     public String getContinuousDriveModeDescription() throws MetadataException
    583     {
    584         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE)) return null;
    585         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE);
    586         switch (value) {
    587             case 0:
    588                 if (_directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY) == 0) {
    589                     return "Single shot";
    590                 } else {
    591                     return "Single shot with self-timer";
    592                 }
     686    @Nullable
     687    public String getContinuousDriveModeDescription()
     688    {
     689        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE);
     690        if (value==null)
     691            return null;
     692        switch (value) {
     693            case 0:
     694                final Integer delay = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY);
     695                if (delay!=null)
     696                    return delay == 0 ? "Single shot" : "Single shot with self-timer";
    593697            case 1:
    594698                return "Continuous";
    595             default:
    596                 return "Unknown (" + value + ")";
    597         }
    598     }
    599 
    600     public String getFlashModeDescription() throws MetadataException
    601     {
    602         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE)) return null;
    603         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE);
     699        }
     700        return "Unknown (" + value + ")";
     701    }
     702
     703    @Nullable
     704    public String getFlashModeDescription()
     705    {
     706        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE);
     707        if (value==null)
     708            return null;
    604709        switch (value) {
    605710            case 0:
     
    619724            case 16:
    620725                // note: this value not set on Canon D30
    621                 return "Extenal flash";
    622             default:
    623                 return "Unknown (" + value + ")";
    624         }
    625     }
    626 
    627     public String getSelfTimerDelayDescription() throws MetadataException
    628     {
    629         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY)) return null;
    630         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY);
     726                return "External flash";
     727            default:
     728                return "Unknown (" + value + ")";
     729        }
     730    }
     731
     732    @Nullable
     733    public String getSelfTimerDelayDescription()
     734    {
     735        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY);
     736        if (value==null)
     737            return null;
    631738        if (value == 0) {
    632739            return "Self timer not used";
     
    637744    }
    638745
    639     public String getMacroModeDescription() throws MetadataException
    640     {
    641         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE)) return null;
    642         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE);
     746    @Nullable
     747    public String getMacroModeDescription()
     748    {
     749        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE);
     750        if (value==null)
     751            return null;
    643752        switch (value) {
    644753            case 1:
     
    651760    }
    652761
    653     public String getQualityDescription() throws MetadataException
    654     {
    655         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY)) return null;
    656         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY);
     762    @Nullable
     763    public String getQualityDescription()
     764    {
     765        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_QUALITY);
     766        if (value==null)
     767            return null;
    657768        switch (value) {
    658769            case 2:
     
    667778    }
    668779
    669     public String getDigitalZoomDescription() throws MetadataException
    670     {
    671         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM)) return null;
    672         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM);
     780    @Nullable
     781    public String getDigitalZoomDescription()
     782    {
     783        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM);
     784        if (value==null)
     785            return null;
    673786        switch (value) {
    674787            case 0:
     
    683796    }
    684797
    685     public String getFocusTypeDescription() throws MetadataException
    686     {
    687         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE)) return null;
    688         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE);
     798    @Nullable
     799    public String getFocusTypeDescription()
     800    {
     801        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE);
     802        if (value==null)
     803            return null;
    689804        switch (value) {
    690805            case 0:
     
    701816    }
    702817
    703     public String getFlashActivityDescription() throws MetadataException
    704     {
    705         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY)) return null;
    706         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY);
     818    @Nullable
     819    public String getFlashActivityDescription()
     820    {
     821        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_ACTIVITY);
     822        if (value==null)
     823            return null;
    707824        switch (value) {
    708825            case 0:
  • trunk/src/com/drew/metadata/exif/CanonMakernoteDirectory.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 27-Nov-2002 10:10:47 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2732 *
    2833 * Many tag definitions explained here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
     34 *
     35 * @author Drew Noakes http://drewnoakes.com
    2936 */
    3037public class CanonMakernoteDirectory extends Directory
    3138{
    32     // CANON cameras have some funny bespoke fields that need further processing...
    33     public static final int TAG_CANON_CAMERA_STATE_1 = 0x0001;
    34     public static final int TAG_CANON_CAMERA_STATE_2 = 0x0004;
    35 
    36     public static final int TAG_CANON_IMAGE_TYPE = 0x0006;
    37     public static final int TAG_CANON_FIRMWARE_VERSION = 0x0007;
    38     public static final int TAG_CANON_IMAGE_NUMBER = 0x0008;
    39     public static final int TAG_CANON_OWNER_NAME = 0x0009;
    40     /**
    41      * To display serial number as on camera use: printf( "%04X%05d", highbyte, lowbyte )
    42      * TODO handle this in CanonMakernoteDescriptor
    43      */
    44     public static final int TAG_CANON_SERIAL_NUMBER = 0x000C;
    45     public static final int TAG_CANON_UNKNOWN_1 = 0x000D;
    46     public static final int TAG_CANON_CUSTOM_FUNCTIONS = 0x000F;
    47 
    48     // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    49     /**
    50      * 1 = Macro
    51      * 2 = Normal
    52      */
    53     public static final int TAG_CANON_STATE1_MACRO_MODE = 0xC101;
    54     public static final int TAG_CANON_STATE1_SELF_TIMER_DELAY = 0xC102;
    55     /**
    56      * 2 = Normal
    57      * 3 = Fine
    58      * 5 = Superfine
    59      */
    60     public static final int TAG_CANON_STATE1_QUALITY = 0xC103;
    61     /**
    62      * 0 = Flash Not Fired
    63      * 1 = Auto
    64      * 2 = On
    65      * 3 = Red Eye Reduction
    66      * 4 = Slow Synchro
    67      * 5 = Auto + Red Eye Reduction
    68      * 6 = On + Red Eye Reduction
    69      * 16 = External Flash
    70      */
    71     public static final int TAG_CANON_STATE1_FLASH_MODE = 0xC104;
    72     /**
    73      * 0 = Single Frame or Timer Mode
    74      * 1 = Continuous
    75      */
    76     public static final int TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE = 0xC105;
    77     public static final int TAG_CANON_STATE1_UNKNOWN_2 = 0xC106;
    78     /**
    79      * 0 = One-Shot
    80      * 1 = AI Servo
    81      * 2 = AI Focus
    82      * 3 = Manual Focus
    83      * 4 = Single
    84      * 5 = Continuous
    85      * 6 = Manual Focus
    86      */
    87     public static final int TAG_CANON_STATE1_FOCUS_MODE_1 = 0xC107;
    88     public static final int TAG_CANON_STATE1_UNKNOWN_3 = 0xC108;
    89     public static final int TAG_CANON_STATE1_UNKNOWN_4 = 0xC109;
    90     /**
    91      * 0 = Large
    92      * 1 = Medium
    93      * 2 = Small
    94      */
    95     public static final int TAG_CANON_STATE1_IMAGE_SIZE = 0xC10A;
    96     /**
    97      * 0 = Full Auto
    98      * 1 = Manual
    99      * 2 = Landscape
    100      * 3 = Fast Shutter
    101      * 4 = Slow Shutter
    102      * 5 = Night
    103      * 6 = Black & White
    104      * 7 = Sepia
    105      * 8 = Portrait
    106      * 9 = Sports
    107      * 10 = Macro / Close-Up
    108      * 11 = Pan Focus
    109      */
    110     public static final int TAG_CANON_STATE1_EASY_SHOOTING_MODE = 0xC10B;
    111     /**
    112      * 0 = No Digital Zoom
    113      * 1 = 2x
    114      * 2 = 4x
    115      */
    116     public static final int TAG_CANON_STATE1_DIGITAL_ZOOM = 0xC10C;
    117     /**
    118      * 0 = Normal
    119      * 1 = High
    120      * 65535 = Low
    121      */
    122     public static final int TAG_CANON_STATE1_CONTRAST = 0xC10D;
    123     /**
    124      * 0 = Normal
    125      * 1 = High
    126      * 65535 = Low
    127      */
    128     public static final int TAG_CANON_STATE1_SATURATION = 0xC10E;
    129     /**
    130      * 0 = Normal
    131      * 1 = High
    132      * 65535 = Low
    133      */
    134     public static final int TAG_CANON_STATE1_SHARPNESS = 0xC10F;
    135     /**
    136      * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
    137      * 15 = Auto ISO
    138      * 16 = ISO 50
    139      * 17 = ISO 100
    140      * 18 = ISO 200
    141      * 19 = ISO 400
    142      */
    143     public static final int TAG_CANON_STATE1_ISO = 0xC110;
    144     /**
    145      * 3 = Evaluative
    146      * 4 = Partial
    147      * 5 = Centre Weighted
    148      */
    149     public static final int TAG_CANON_STATE1_METERING_MODE = 0xC111;
    150     /**
    151      * 0 = Manual
    152      * 1 = Auto
    153      * 3 = Close-up (Macro)
    154      * 8 = Locked (Pan Mode)
    155      */
    156     public static final int TAG_CANON_STATE1_FOCUS_TYPE = 0xC112;
    157     /**
    158      * 12288 = None (Manual Focus)
    159      * 12289 = Auto Selected
    160      * 12290 = Right
    161      * 12291 = Centre
    162      * 12292 = Left
    163      */
    164     public static final int TAG_CANON_STATE1_AF_POINT_SELECTED = 0xC113;
    165     /**
    166      * 0 = Easy Shooting (See Easy Shooting Mode)
    167      * 1 = Program
    168      * 2 = Tv-Priority
    169      * 3 = Av-Priority
    170      * 4 = Manual
    171      * 5 = A-DEP
    172      */
    173     public static final int TAG_CANON_STATE1_EXPOSURE_MODE = 0xC114;
    174     public static final int TAG_CANON_STATE1_UNKNOWN_7 = 0xC115;
    175     public static final int TAG_CANON_STATE1_UNKNOWN_8 = 0xC116;
    176     public static final int TAG_CANON_STATE1_LONG_FOCAL_LENGTH = 0xC117;
    177     public static final int TAG_CANON_STATE1_SHORT_FOCAL_LENGTH = 0xC118;
    178     public static final int TAG_CANON_STATE1_FOCAL_UNITS_PER_MM = 0xC119;
    179     public static final int TAG_CANON_STATE1_UNKNOWN_9 = 0xC11A;
    180     public static final int TAG_CANON_STATE1_UNKNOWN_10 = 0xC11B;
    181     /**
    182      * 0 = Flash Did Not Fire
    183      * 1 = Flash Fired
    184      */
    185     public static final int TAG_CANON_STATE1_FLASH_ACTIVITY = 0xC11C;
    186     public static final int TAG_CANON_STATE1_FLASH_DETAILS = 0xC11D;
    187     public static final int TAG_CANON_STATE1_UNKNOWN_12 = 0xC11E;
    188     public static final int TAG_CANON_STATE1_UNKNOWN_13 = 0xC11F;
    189     /**
    190      * 0 = Focus Mode: Single
    191      * 1 = Focus Mode: Continuous
    192      */
    193     public static final int TAG_CANON_STATE1_FOCUS_MODE_2 = 0xC120;
    194 
    195     /**
    196      * 0 = Auto
    197      * 1 = Sunny
    198      * 2 = Cloudy
    199      * 3 = Tungsten
    200      * 4 = Flourescent
    201      * 5 = Flash
    202      * 6 = Custom
    203      */
    204     public static final int TAG_CANON_STATE2_WHITE_BALANCE = 0xC207;
    205     public static final int TAG_CANON_STATE2_SEQUENCE_NUMBER = 0xC209;
    206     public static final int TAG_CANON_STATE2_AF_POINT_USED = 0xC20E;
    207     /**
    208      * The value of this tag may be translated into a flash bias value, in EV.
    209      *
    210      * 0xffc0 = -2 EV
    211      * 0xffcc = -1.67 EV
    212      * 0xffd0 = -1.5 EV
    213      * 0xffd4 = -1.33 EV
    214      * 0xffe0 = -1 EV
    215      * 0xffec = -0.67 EV
    216      * 0xfff0 = -0.5 EV
    217      * 0xfff4 = -0.33 EV
    218      * 0x0000 = 0 EV
    219      * 0x000c = 0.33 EV
    220      * 0x0010 = 0.5 EV
    221      * 0x0014 = 0.67 EV
    222      * 0x0020 = 1 EV
    223      * 0x002c = 1.33 EV
    224      * 0x0030 = 1.5 EV
    225      * 0x0034 = 1.67 EV
    226      * 0x0040 = 2 EV
    227      */
    228     public static final int TAG_CANON_STATE2_FLASH_BIAS = 0xC20F;
    229     public static final int TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING = 0xC210;
    230     public static final int TAG_CANON_STATE2_AEB_BRACKET_VALUE = 0xC211;
    231     public static final int TAG_CANON_STATE2_SUBJECT_DISTANCE = 0xC213;
    232 
    233     /**
    234      * Long Exposure Noise Reduction
    235      * 0 = Off
    236      * 1 = On
    237      */
    238     public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
    239 
    240     /**
    241      * Shutter/Auto Exposure-lock buttons
    242      * 0 = AF/AE lock
    243      * 1 = AE lock/AF
    244      * 2 = AF/AF lock
    245      * 3 = AE+release/AE+AF
    246      */
    247     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
    248 
    249     /**
    250      * Mirror lockup
    251      * 0 = Disable
    252      * 1 = Enable
    253      */
    254     public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
    255 
    256     /**
    257      * Tv/Av and exposure level
    258      * 0 = 1/2 stop
    259      * 1 = 1/3 stop
    260      */
    261     public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
    262 
    263     /**
    264      * AF-assist light
    265      * 0 = On (Auto)
    266      * 1 = Off
    267      */
    268     public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
    269 
    270     /**
    271      * Shutter speed in Av mode
    272      * 0 = Automatic
    273      * 1 = 1/200 (fixed)
    274      */
    275     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
    276 
    277     /**
    278      * Auto-Exposure Bracketting sequence/auto cancellation
    279      * 0 = 0,-,+ / Enabled
    280      * 1 = 0,-,+ / Disabled
    281      * 2 = -,0,+ / Enabled
    282      * 3 = -,0,+ / Disabled
    283      */
    284     public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307;
    285 
    286     /**
    287      * Shutter Curtain Sync
    288      * 0 = 1st Curtain Sync
    289      * 1 = 2nd Curtain Sync
    290      */
    291     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
    292 
    293     /**
    294      * Lens Auto-Focus stop button Function Switch
    295      * 0 = AF stop
    296      * 1 = Operate AF
    297      * 2 = Lock AE and start timer
    298      */
    299     public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
    300 
    301     /**
    302      * Auto reduction of fill flash
    303      * 0 = Enable
    304      * 1 = Disable
    305      */
    306     public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
    307 
    308     /**
    309      * Menu button return position
    310      * 0 = Top
    311      * 1 = Previous (volatile)
    312      * 2 = Previous
    313      */
    314     public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
    315 
    316     /**
    317      * SET button function when shooting
    318      * 0 = Not Assigned
    319      * 1 = Change Quality
    320      * 2 = Change ISO Speed
    321      * 3 = Select Parameters
    322      */
    323     public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;
    324 
    325     /**
    326      * Sensor cleaning
    327      * 0 = Disable
    328      * 1 = Enable
    329      */
    330     public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;
     39    // These TAG_*_ARRAY Exif tags map to arrays of int16 values which are split out into separate 'fake' tags.
     40    // When an attempt is made to set one of these on the directory, it is split and the corresponding offset added to the tagType.
     41    // So the resulting tag is the offset + the index into the array.
     42
     43    private static final int TAG_CAMERA_SETTINGS_ARRAY  = 0x0001;
     44    private static final int TAG_FOCAL_LENGTH_ARRAY = 0x0002;
     45    private static final int TAG_SHOT_INFO_ARRAY = 0x0004;
     46    private static final int TAG_PANORAMA_ARRAY = 0x0005;
     47
     48    public static final int TAG_CANON_IMAGE_TYPE            = 0x0006;
     49    public static final int TAG_CANON_FIRMWARE_VERSION      = 0x0007;
     50    public static final int TAG_CANON_IMAGE_NUMBER          = 0x0008;
     51    public static final int TAG_CANON_OWNER_NAME            = 0x0009;
     52    public static final int TAG_CANON_SERIAL_NUMBER         = 0x000C;
     53    public static final int TAG_CAMERA_INFO_ARRAY           = 0x000D; // depends upon model, so leave for now
     54    public static final int TAG_CANON_FILE_LENGTH           = 0x000E;
     55    public static final int TAG_CANON_CUSTOM_FUNCTIONS_ARRAY = 0x000F; // depends upon model, so leave for now
     56    public static final int TAG_MODEL_ID                    = 0x0010;
     57    public static final int TAG_MOVIE_INFO_ARRAY            = 0x0011; // not currently decoded as not sure we see it in still images
     58    private static final int TAG_AF_INFO_ARRAY              = 0x0012; // not currently decoded
     59    public static final int TAG_THUMBNAIL_IMAGE_VALID_AREA  = 0x0013;
     60    public static final int TAG_SERIAL_NUMBER_FORMAT        = 0x0015;
     61    public static final int TAG_SUPER_MACRO                 = 0x001A;
     62    public static final int TAG_DATE_STAMP_MODE             = 0x001C;
     63    public static final int TAG_MY_COLORS                   = 0x001D;
     64    public static final int TAG_FIRMWARE_REVISION           = 0x001E;
     65    public static final int TAG_CATEGORIES                  = 0x0023;
     66    public static final int TAG_FACE_DETECT_ARRAY_1         = 0x0024;
     67    public static final int TAG_FACE_DETECT_ARRAY_2         = 0x0025;
     68    public static final int TAG_AF_INFO_ARRAY_2             = 0x0026;
     69    public static final int TAG_IMAGE_UNIQUE_ID             = 0x0028;
     70    public static final int TAG_RAW_DATA_OFFSET             = 0x0081;
     71    public static final int TAG_ORIGINAL_DECISION_DATA_OFFSET = 0x0083;
     72    public static final int TAG_CUSTOM_FUNCTIONS_1D_ARRAY   = 0x0090; // not currently decoded
     73    public static final int TAG_PERSONAL_FUNCTIONS_ARRAY    = 0x0091; // not currently decoded
     74    public static final int TAG_PERSONAL_FUNCTION_VALUES_ARRAY = 0x0092; // not currently decoded
     75    public static final int TAG_FILE_INFO_ARRAY             = 0x0093; // not currently decoded
     76    public static final int TAG_AF_POINTS_IN_FOCUS_1D       = 0x0094;
     77    public static final int TAG_LENS_MODEL                  = 0x0095;
     78    public static final int TAG_SERIAL_INFO_ARRAY           = 0x0096; // not currently decoded
     79    public static final int TAG_DUST_REMOVAL_DATA           = 0x0097;
     80    public static final int TAG_CROP_INFO                   = 0x0098; // not currently decoded
     81    public static final int TAG_CUSTOM_FUNCTIONS_ARRAY_2    = 0x0099; // not currently decoded
     82    public static final int TAG_ASPECT_INFO_ARRAY           = 0x009A; // not currently decoded
     83    public static final int TAG_PROCESSING_INFO_ARRAY       = 0x00A0; // not currently decoded
     84    public static final int TAG_TONE_CURVE_TABLE            = 0x00A1;
     85    public static final int TAG_SHARPNESS_TABLE             = 0x00A2;
     86    public static final int TAG_SHARPNESS_FREQ_TABLE        = 0x00A3;
     87    public static final int TAG_WHITE_BALANCE_TABLE         = 0x00A4;
     88    public static final int TAG_COLOR_BALANCE_ARRAY         = 0x00A9; // not currently decoded
     89    public static final int TAG_MEASURED_COLOR_ARRAY        = 0x00AA; // not currently decoded
     90    public static final int TAG_COLOR_TEMPERATURE           = 0x00AE;
     91    public static final int TAG_CANON_FLAGS_ARRAY           = 0x00B0; // not currently decoded
     92    public static final int TAG_MODIFIED_INFO_ARRAY         = 0x00B1; // not currently decoded
     93    public static final int TAG_TONE_CURVE_MATCHING         = 0x00B2;
     94    public static final int TAG_WHITE_BALANCE_MATCHING      = 0x00B3;
     95    public static final int TAG_COLOR_SPACE                 = 0x00B4;
     96    public static final int TAG_PREVIEW_IMAGE_INFO_ARRAY    = 0x00B6; // not currently decoded
     97    public static final int TAG_VRD_OFFSET                  = 0x00D0;
     98    public static final int TAG_SENSOR_INFO_ARRAY           = 0x00E0; // not currently decoded
     99    public static final int TAG_COLOR_DATA_ARRAY_2 = 0x4001; // depends upon camera model, not currently decoded
     100    public static final int TAG_COLOR_INFO_ARRAY_2          = 0x4003; // not currently decoded
     101    public static final int TAG_CUSTOM_PICTURE_STYLE_FILE_NAME = 0x4010;
     102    public static final int TAG_COLOR_INFO_ARRAY            = 0x4013; // not currently decoded
     103    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_1 = 0x4015; // not currently decoded
     104    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_2 = 0x4016; // not currently decoded
     105    public static final int TAG_LIGHTING_OPTIMIZER_ARRAY = 0x4018; // not currently decoded
     106    public static final int TAG_LENS_INFO_ARRAY             = 0x4019; // not currently decoded
     107    public static final int TAG_AMBIANCE_INFO_ARRAY         = 0x4020; // not currently decoded
     108    public static final int TAG_FILTER_INFO_ARRAY           = 0x4024; // not currently decoded
     109
     110    public final static class CameraSettings
     111    {
     112        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     113        private static final int OFFSET = 0xC100;
     114
     115        /**
     116         * 1 = Macro
     117         * 2 = Normal
     118         */
     119        public static final int TAG_MACRO_MODE = OFFSET + 0x01;
     120        public static final int TAG_SELF_TIMER_DELAY = OFFSET + 0x02;
     121        /**
     122         * 2 = Normal
     123         * 3 = Fine
     124         * 5 = Superfine
     125         */
     126        public static final int TAG_QUALITY = OFFSET + 0x03;
     127        /**
     128         * 0 = Flash Not Fired
     129         * 1 = Auto
     130         * 2 = On
     131         * 3 = Red Eye Reduction
     132         * 4 = Slow Synchro
     133         * 5 = Auto + Red Eye Reduction
     134         * 6 = On + Red Eye Reduction
     135         * 16 = External Flash
     136         */
     137        public static final int TAG_FLASH_MODE = OFFSET + 0x04;
     138        /**
     139         * 0 = Single Frame or Timer Mode
     140         * 1 = Continuous
     141         */
     142        public static final int TAG_CONTINUOUS_DRIVE_MODE = OFFSET + 0x05;
     143        public static final int TAG_UNKNOWN_2 = OFFSET + 0x06;
     144        /**
     145         * 0 = One-Shot
     146         * 1 = AI Servo
     147         * 2 = AI Focus
     148         * 3 = Manual Focus
     149         * 4 = Single
     150         * 5 = Continuous
     151         * 6 = Manual Focus
     152         */
     153        public static final int TAG_FOCUS_MODE_1 = OFFSET + 0x07;
     154        public static final int TAG_UNKNOWN_3 = OFFSET + 0x08;
     155        public static final int TAG_UNKNOWN_4 = OFFSET + 0x09;
     156        /**
     157         * 0 = Large
     158         * 1 = Medium
     159         * 2 = Small
     160         */
     161        public static final int TAG_IMAGE_SIZE = OFFSET + 0x0A;
     162        /**
     163         * 0 = Full Auto
     164         * 1 = Manual
     165         * 2 = Landscape
     166         * 3 = Fast Shutter
     167         * 4 = Slow Shutter
     168         * 5 = Night
     169         * 6 = Black & White
     170         * 7 = Sepia
     171         * 8 = Portrait
     172         * 9 = Sports
     173         * 10 = Macro / Close-Up
     174         * 11 = Pan Focus
     175         */
     176        public static final int TAG_EASY_SHOOTING_MODE = OFFSET + 0x0B;
     177        /**
     178         * 0 = No Digital Zoom
     179         * 1 = 2x
     180         * 2 = 4x
     181         */
     182        public static final int TAG_DIGITAL_ZOOM = OFFSET + 0x0C;
     183        /**
     184         * 0 = Normal
     185         * 1 = High
     186         * 65535 = Low
     187         */
     188        public static final int TAG_CONTRAST = OFFSET + 0x0D;
     189        /**
     190         * 0 = Normal
     191         * 1 = High
     192         * 65535 = Low
     193         */
     194        public static final int TAG_SATURATION = OFFSET + 0x0E;
     195        /**
     196         * 0 = Normal
     197         * 1 = High
     198         * 65535 = Low
     199         */
     200        public static final int TAG_SHARPNESS = OFFSET + 0x0F;
     201        /**
     202         * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
     203         * 15 = Auto ISO
     204         * 16 = ISO 50
     205         * 17 = ISO 100
     206         * 18 = ISO 200
     207         * 19 = ISO 400
     208         */
     209        public static final int TAG_ISO = OFFSET + 0x10;
     210        /**
     211         * 3 = Evaluative
     212         * 4 = Partial
     213         * 5 = Centre Weighted
     214         */
     215        public static final int TAG_METERING_MODE = OFFSET + 0x11;
     216        /**
     217         * 0 = Manual
     218         * 1 = Auto
     219         * 3 = Close-up (Macro)
     220         * 8 = Locked (Pan Mode)
     221         */
     222        public static final int TAG_FOCUS_TYPE = OFFSET + 0x12;
     223        /**
     224         * 12288 = None (Manual Focus)
     225         * 12289 = Auto Selected
     226         * 12290 = Right
     227         * 12291 = Centre
     228         * 12292 = Left
     229         */
     230        public static final int TAG_AF_POINT_SELECTED = OFFSET + 0x13;
     231        /**
     232         * 0 = Easy Shooting (See Easy Shooting Mode)
     233         * 1 = Program
     234         * 2 = Tv-Priority
     235         * 3 = Av-Priority
     236         * 4 = Manual
     237         * 5 = A-DEP
     238         */
     239        public static final int TAG_EXPOSURE_MODE = OFFSET + 0x14;
     240        public static final int TAG_UNKNOWN_7 = OFFSET + 0x15;
     241        public static final int TAG_UNKNOWN_8 = OFFSET + 0x16;
     242        public static final int TAG_LONG_FOCAL_LENGTH = OFFSET + 0x17;
     243        public static final int TAG_SHORT_FOCAL_LENGTH = OFFSET + 0x18;
     244        public static final int TAG_FOCAL_UNITS_PER_MM = OFFSET + 0x19;
     245        public static final int TAG_UNKNOWN_9 = OFFSET + 0x1A;
     246        public static final int TAG_UNKNOWN_10 = OFFSET + 0x1B;
     247        /**
     248         * 0 = Flash Did Not Fire
     249         * 1 = Flash Fired
     250         */
     251        public static final int TAG_FLASH_ACTIVITY = OFFSET + 0x1C;
     252        public static final int TAG_FLASH_DETAILS = OFFSET + 0x1D;
     253        public static final int TAG_UNKNOWN_12 = OFFSET + 0x1E;
     254        public static final int TAG_UNKNOWN_13 = OFFSET + 0x1F;
     255        /**
     256         * 0 = Focus Mode: Single
     257         * 1 = Focus Mode: Continuous
     258         */
     259        public static final int TAG_FOCUS_MODE_2 = OFFSET + 0x20;
     260    }
     261   
     262    public final static class FocalLength
     263    {
     264        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     265
     266        private static final int OFFSET = 0xC200;
     267       
     268        /**
     269         * 0 = Auto
     270         * 1 = Sunny
     271         * 2 = Cloudy
     272         * 3 = Tungsten
     273         * 4 = Florescent
     274         * 5 = Flash
     275         * 6 = Custom
     276         */
     277        public static final int TAG_WHITE_BALANCE = OFFSET + 0x07;
     278        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 0x09;
     279        public static final int TAG_AF_POINT_USED = OFFSET + 0x0E;
     280        /**
     281         * The value of this tag may be translated into a flash bias value, in EV.
     282         *
     283         * 0xffc0 = -2 EV
     284         * 0xffcc = -1.67 EV
     285         * 0xffd0 = -1.5 EV
     286         * 0xffd4 = -1.33 EV
     287         * 0xffe0 = -1 EV
     288         * 0xffec = -0.67 EV
     289         * 0xfff0 = -0.5 EV
     290         * 0xfff4 = -0.33 EV
     291         * 0x0000 = 0 EV
     292         * 0x000c = 0.33 EV
     293         * 0x0010 = 0.5 EV
     294         * 0x0014 = 0.67 EV
     295         * 0x0020 = 1 EV
     296         * 0x002c = 1.33 EV
     297         * 0x0030 = 1.5 EV
     298         * 0x0034 = 1.67 EV
     299         * 0x0040 = 2 EV
     300         */
     301        public static final int TAG_FLASH_BIAS = OFFSET + 0x0F;
     302        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 0x10;
     303        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 0x11;
     304        public static final int TAG_SUBJECT_DISTANCE = OFFSET + 0x13;
     305    }
     306   
     307    public final static class ShotInfo
     308    {
     309        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     310
     311        private static final int OFFSET = 0xC400;
     312
     313        public static final int TAG_AUTO_ISO = OFFSET + 1;
     314        public static final int TAG_BASE_ISO = OFFSET + 2;
     315        public static final int TAG_MEASURED_EV = OFFSET + 3;
     316        public static final int TAG_TARGET_APERTURE = OFFSET + 4;
     317        public static final int TAG_TARGET_EXPOSURE_TIME = OFFSET + 5;
     318        public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 6;
     319        public static final int TAG_WHITE_BALANCE = OFFSET + 7;
     320        public static final int TAG_SLOW_SHUTTER = OFFSET + 8;
     321        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 9;
     322        public static final int TAG_OPTICAL_ZOOM_CODE = OFFSET + 10;
     323        public static final int TAG_CAMERA_TEMPERATURE = OFFSET + 12;
     324        public static final int TAG_FLASH_GUIDE_NUMBER = OFFSET + 13;
     325        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 14;
     326        public static final int TAG_FLASH_EXPOSURE_BRACKETING = OFFSET + 15;
     327        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 16;
     328        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 17;
     329        public static final int TAG_CONTROL_MODE = OFFSET + 18;
     330        public static final int TAG_FOCUS_DISTANCE_UPPER = OFFSET + 19;
     331        public static final int TAG_FOCUS_DISTANCE_LOWER = OFFSET + 20;
     332        public static final int TAG_F_NUMBER = OFFSET + 21;
     333        public static final int TAG_EXPOSURE_TIME = OFFSET + 22;
     334        public static final int TAG_MEASURED_EV_2 = OFFSET + 23;
     335        public static final int TAG_BULB_DURATION = OFFSET + 24;
     336        public static final int TAG_CAMERA_TYPE = OFFSET + 26;
     337        public static final int TAG_AUTO_ROTATE = OFFSET + 27;
     338        public static final int TAG_ND_FILTER = OFFSET + 28;
     339        public static final int TAG_SELF_TIMER_2 = OFFSET + 29;
     340        public static final int TAG_FLASH_OUTPUT = OFFSET + 33;
     341    }
     342
     343    public final static class Panorama
     344    {
     345        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     346
     347        private static final int OFFSET = 0xC500;
     348
     349        public static final int TAG_PANORAMA_FRAME_NUMBER = OFFSET + 2;
     350        public static final int TAG_PANORAMA_DIRECTION = OFFSET + 5;
     351    }
     352
     353    public final static class AFInfo
     354    {
     355        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     356
     357        private static final int OFFSET = 0xD200;
     358
     359        public static final int TAG_NUM_AF_POINTS = OFFSET;
     360        public static final int TAG_VALID_AF_POINTS = OFFSET + 1;
     361        public static final int TAG_IMAGE_WIDTH = OFFSET + 2;
     362        public static final int TAG_IMAGE_HEIGHT = OFFSET + 3;
     363        public static final int TAG_AF_IMAGE_WIDTH = OFFSET + 4;
     364        public static final int TAG_AF_IMAGE_HEIGHT = OFFSET + 5;
     365        public static final int TAG_AF_AREA_WIDTH = OFFSET + 6;
     366        public static final int TAG_AF_AREA_HEIGHT = OFFSET + 7;
     367        public static final int TAG_AF_AREA_X_POSITIONS = OFFSET + 8;
     368        public static final int TAG_AF_AREA_Y_POSITIONS = OFFSET + 9;
     369        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 10;
     370        public static final int TAG_PRIMARY_AF_POINT_1 = OFFSET + 11;
     371        public static final int TAG_PRIMARY_AF_POINT_2 = OFFSET + 12; // not sure why there are two of these
     372    }
     373
     374//    /**
     375//     * Long Exposure Noise Reduction
     376//     * 0 = Off
     377//     * 1 = On
     378//     */
     379//    public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
     380//
     381//    /**
     382//     * Shutter/Auto Exposure-lock buttons
     383//     * 0 = AF/AE lock
     384//     * 1 = AE lock/AF
     385//     * 2 = AF/AF lock
     386//     * 3 = AE+release/AE+AF
     387//     */
     388//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
     389//
     390//    /**
     391//     * Mirror lockup
     392//     * 0 = Disable
     393//     * 1 = Enable
     394//     */
     395//    public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
     396//
     397//    /**
     398//     * Tv/Av and exposure level
     399//     * 0 = 1/2 stop
     400//     * 1 = 1/3 stop
     401//     */
     402//    public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
     403//
     404//    /**
     405//     * AF-assist light
     406//     * 0 = On (Auto)
     407//     * 1 = Off
     408//     */
     409//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
     410//
     411//    /**
     412//     * Shutter speed in Av mode
     413//     * 0 = Automatic
     414//     * 1 = 1/200 (fixed)
     415//     */
     416//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
     417//
     418//    /**
     419//     * Auto-Exposure Bracketting sequence/auto cancellation
     420//     * 0 = 0,-,+ / Enabled
     421//     * 1 = 0,-,+ / Disabled
     422//     * 2 = -,0,+ / Enabled
     423//     * 3 = -,0,+ / Disabled
     424//     */
     425//    public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307;
     426//
     427//    /**
     428//     * Shutter Curtain Sync
     429//     * 0 = 1st Curtain Sync
     430//     * 1 = 2nd Curtain Sync
     431//     */
     432//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
     433//
     434//    /**
     435//     * Lens Auto-Focus stop button Function Switch
     436//     * 0 = AF stop
     437//     * 1 = Operate AF
     438//     * 2 = Lock AE and start timer
     439//     */
     440//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
     441//
     442//    /**
     443//     * Auto reduction of fill flash
     444//     * 0 = Enable
     445//     * 1 = Disable
     446//     */
     447//    public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
     448//
     449//    /**
     450//     * Menu button return position
     451//     * 0 = Top
     452//     * 1 = Previous (volatile)
     453//     * 2 = Previous
     454//     */
     455//    public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
     456//
     457//    /**
     458//     * SET button function when shooting
     459//     * 0 = Not Assigned
     460//     * 1 = Change Quality
     461//     * 2 = Change ISO Speed
     462//     * 3 = Select Parameters
     463//     */
     464//    public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;
     465//
     466//    /**
     467//     * Sensor cleaning
     468//     * 0 = Disable
     469//     * 1 = Enable
     470//     */
     471//    public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;
    331472
    332473    // 9  A  B  C  D  E  F  10 11 12 13
    333474    // 9  10 11 12 13 14 15 16 17 18 19
    334     protected static final HashMap _tagNameMap = new HashMap();
     475    @NotNull
     476    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    335477
    336478    static
    337479    {
    338         _tagNameMap.put(new Integer(TAG_CANON_FIRMWARE_VERSION), "Firmware Version");
    339         _tagNameMap.put(new Integer(TAG_CANON_IMAGE_NUMBER), "Image Number");
    340         _tagNameMap.put(new Integer(TAG_CANON_IMAGE_TYPE), "Image Type");
    341         _tagNameMap.put(new Integer(TAG_CANON_OWNER_NAME), "Owner Name");
    342         _tagNameMap.put(new Integer(TAG_CANON_UNKNOWN_1), "Makernote Unknown 1");
    343         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTIONS), "Custom Functions");
    344         _tagNameMap.put(new Integer(TAG_CANON_SERIAL_NUMBER), "Camera Serial Number");
    345         _tagNameMap.put(new Integer(TAG_CANON_STATE1_AF_POINT_SELECTED), "AF Point Selected");
    346         _tagNameMap.put(new Integer(TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE), "Continuous Drive Mode");
    347         _tagNameMap.put(new Integer(TAG_CANON_STATE1_CONTRAST), "Contrast");
    348         _tagNameMap.put(new Integer(TAG_CANON_STATE1_EASY_SHOOTING_MODE), "Easy Shooting Mode");
    349         _tagNameMap.put(new Integer(TAG_CANON_STATE1_EXPOSURE_MODE), "Exposure Mode");
    350         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_DETAILS), "Flash Details");
    351         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_MODE), "Flash Mode");
    352         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCAL_UNITS_PER_MM), "Focal Units per mm");
    353         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_MODE_1), "Focus Mode");
    354         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_MODE_2), "Focus Mode");
    355         _tagNameMap.put(new Integer(TAG_CANON_STATE1_IMAGE_SIZE), "Image Size");
    356         _tagNameMap.put(new Integer(TAG_CANON_STATE1_ISO), "Iso");
    357         _tagNameMap.put(new Integer(TAG_CANON_STATE1_LONG_FOCAL_LENGTH), "Long Focal Length");
    358         _tagNameMap.put(new Integer(TAG_CANON_STATE1_MACRO_MODE), "Macro Mode");
    359         _tagNameMap.put(new Integer(TAG_CANON_STATE1_METERING_MODE), "Metering Mode");
    360         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SATURATION), "Saturation");
    361         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SELF_TIMER_DELAY), "Self Timer Delay");
    362         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SHARPNESS), "Sharpness");
    363         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SHORT_FOCAL_LENGTH), "Short Focal Length");
    364         _tagNameMap.put(new Integer(TAG_CANON_STATE1_QUALITY), "Quality");
    365         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_2), "Unknown Camera State 2");
    366         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_3), "Unknown Camera State 3");
    367         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_4), "Unknown Camera State 4");
    368         _tagNameMap.put(new Integer(TAG_CANON_STATE1_DIGITAL_ZOOM), "Digital Zoom");
    369         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_TYPE), "Focus Type");
    370         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_7), "Unknown Camera State 7");
    371         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_8), "Unknown Camera State 8");
    372         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_9), "Unknown Camera State 9");
    373         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_10), "Unknown Camera State 10");
    374         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_ACTIVITY), "Flash Activity");
    375         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_12), "Unknown Camera State 12");
    376         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_13), "Unknown Camera State 13");
    377         _tagNameMap.put(new Integer(TAG_CANON_STATE2_WHITE_BALANCE), "White Balance");
    378         _tagNameMap.put(new Integer(TAG_CANON_STATE2_SEQUENCE_NUMBER), "Sequence Number");
    379         _tagNameMap.put(new Integer(TAG_CANON_STATE2_AF_POINT_USED), "AF Point Used");
    380         _tagNameMap.put(new Integer(TAG_CANON_STATE2_FLASH_BIAS), "Flash Bias");
    381         _tagNameMap.put(new Integer(TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING), "Auto Exposure Bracketing");
    382         _tagNameMap.put(new Integer(TAG_CANON_STATE2_AEB_BRACKET_VALUE), "AEB Bracket Value");
    383         _tagNameMap.put(new Integer(TAG_CANON_STATE2_SUBJECT_DISTANCE), "Subject Distance");
    384 
    385         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION), "Long Exposure Noise Reduction");
    386         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS), "Shutter/Auto Exposure-lock Buttons");
    387         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP), "Mirror Lockup");
    388         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL), "Tv/Av And Exposure Level");
    389         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT), "AF-Assist Light");
    390         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE), "Shutter Speed in Av Mode");
    391         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING), "Auto-Exposure Bracketting Sequence/Auto Cancellation");
    392         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC), "Shutter Curtain Sync");
    393         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_STOP), "Lens Auto-Focus Stop Button Function Switch");
    394         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION), "Auto Reduction of Fill Flash");
    395         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN), "Menu Button Return Position");
    396         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION), "SET Button Function When Shooting");
    397         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING), "Sensor Cleaning");
     480        _tagNameMap.put(TAG_CANON_FIRMWARE_VERSION, "Firmware Version");
     481        _tagNameMap.put(TAG_CANON_IMAGE_NUMBER, "Image Number");
     482        _tagNameMap.put(TAG_CANON_IMAGE_TYPE, "Image Type");
     483        _tagNameMap.put(TAG_CANON_OWNER_NAME, "Owner Name");
     484        _tagNameMap.put(TAG_CANON_SERIAL_NUMBER, "Camera Serial Number");
     485        _tagNameMap.put(TAG_CAMERA_INFO_ARRAY, "Camera Info Array");
     486        _tagNameMap.put(TAG_CANON_FILE_LENGTH, "File Length");
     487        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTIONS_ARRAY, "Custom Functions");
     488        _tagNameMap.put(TAG_MODEL_ID, "Canon Model ID");
     489        _tagNameMap.put(TAG_MOVIE_INFO_ARRAY, "Movie Info Array");
     490
     491        _tagNameMap.put(CameraSettings.TAG_AF_POINT_SELECTED, "AF Point Selected");
     492        _tagNameMap.put(CameraSettings.TAG_CONTINUOUS_DRIVE_MODE, "Continuous Drive Mode");
     493        _tagNameMap.put(CameraSettings.TAG_CONTRAST, "Contrast");
     494        _tagNameMap.put(CameraSettings.TAG_EASY_SHOOTING_MODE, "Easy Shooting Mode");
     495        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode");
     496        _tagNameMap.put(CameraSettings.TAG_FLASH_DETAILS, "Flash Details");
     497        _tagNameMap.put(CameraSettings.TAG_FLASH_MODE, "Flash Mode");
     498        _tagNameMap.put(CameraSettings.TAG_FOCAL_UNITS_PER_MM, "Focal Units per mm");
     499        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_1, "Focus Mode");
     500        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_2, "Focus Mode");
     501        _tagNameMap.put(CameraSettings.TAG_IMAGE_SIZE, "Image Size");
     502        _tagNameMap.put(CameraSettings.TAG_ISO, "Iso");
     503        _tagNameMap.put(CameraSettings.TAG_LONG_FOCAL_LENGTH, "Long Focal Length");
     504        _tagNameMap.put(CameraSettings.TAG_MACRO_MODE, "Macro Mode");
     505        _tagNameMap.put(CameraSettings.TAG_METERING_MODE, "Metering Mode");
     506        _tagNameMap.put(CameraSettings.TAG_SATURATION, "Saturation");
     507        _tagNameMap.put(CameraSettings.TAG_SELF_TIMER_DELAY, "Self Timer Delay");
     508        _tagNameMap.put(CameraSettings.TAG_SHARPNESS, "Sharpness");
     509        _tagNameMap.put(CameraSettings.TAG_SHORT_FOCAL_LENGTH, "Short Focal Length");
     510        _tagNameMap.put(CameraSettings.TAG_QUALITY, "Quality");
     511        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_2, "Unknown Camera Setting 2");
     512        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_3, "Unknown Camera Setting 3");
     513        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_4, "Unknown Camera Setting 4");
     514        _tagNameMap.put(CameraSettings.TAG_DIGITAL_ZOOM, "Digital Zoom");
     515        _tagNameMap.put(CameraSettings.TAG_FOCUS_TYPE, "Focus Type");
     516        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_7, "Unknown Camera Setting 7");
     517        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_8, "Unknown Camera Setting 8");
     518        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_9, "Unknown Camera Setting 9");
     519        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_10, "Unknown Camera Setting 10");
     520        _tagNameMap.put(CameraSettings.TAG_FLASH_ACTIVITY, "Flash Activity");
     521        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_12, "Unknown Camera Setting 12");
     522        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_13, "Unknown Camera Setting 13");
     523
     524        _tagNameMap.put(FocalLength.TAG_WHITE_BALANCE, "White Balance");
     525        _tagNameMap.put(FocalLength.TAG_SEQUENCE_NUMBER, "Sequence Number");
     526        _tagNameMap.put(FocalLength.TAG_AF_POINT_USED, "AF Point Used");
     527        _tagNameMap.put(FocalLength.TAG_FLASH_BIAS, "Flash Bias");
     528        _tagNameMap.put(FocalLength.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
     529        _tagNameMap.put(FocalLength.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
     530        _tagNameMap.put(FocalLength.TAG_SUBJECT_DISTANCE, "Subject Distance");
     531
     532        _tagNameMap.put(ShotInfo.TAG_AUTO_ISO, "Auto ISO");
     533        _tagNameMap.put(ShotInfo.TAG_BASE_ISO, "Base ISO");
     534        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV, "Measured EV");
     535        _tagNameMap.put(ShotInfo.TAG_TARGET_APERTURE, "Target Aperture");
     536        _tagNameMap.put(ShotInfo.TAG_TARGET_EXPOSURE_TIME, "Target Exposure Time");
     537        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_COMPENSATION, "Exposure Compensation");
     538        _tagNameMap.put(ShotInfo.TAG_WHITE_BALANCE, "White Balance");
     539        _tagNameMap.put(ShotInfo.TAG_SLOW_SHUTTER, "Slow Shutter");
     540        _tagNameMap.put(ShotInfo.TAG_SEQUENCE_NUMBER, "Sequence Number");
     541        _tagNameMap.put(ShotInfo.TAG_OPTICAL_ZOOM_CODE, "Optical Zoom Code");
     542        _tagNameMap.put(ShotInfo.TAG_CAMERA_TEMPERATURE, "Camera Temperature");
     543        _tagNameMap.put(ShotInfo.TAG_FLASH_GUIDE_NUMBER, "Flash Guide Number");
     544        _tagNameMap.put(ShotInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus");
     545        _tagNameMap.put(ShotInfo.TAG_FLASH_EXPOSURE_BRACKETING, "Flash Exposure Compensation");
     546        _tagNameMap.put(ShotInfo.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
     547        _tagNameMap.put(ShotInfo.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
     548        _tagNameMap.put(ShotInfo.TAG_CONTROL_MODE, "Control Mode");
     549        _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_UPPER, "Focus Distance Upper");
     550        _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_LOWER, "Focus Distance Lower");
     551        _tagNameMap.put(ShotInfo.TAG_F_NUMBER, "F Number");
     552        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_TIME, "Exposure Time");
     553        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV_2, "Measured EV 2");
     554        _tagNameMap.put(ShotInfo.TAG_BULB_DURATION, "Bulb Duration");
     555        _tagNameMap.put(ShotInfo.TAG_CAMERA_TYPE, "Camera Type");
     556        _tagNameMap.put(ShotInfo.TAG_AUTO_ROTATE, "Auto Rotate");
     557        _tagNameMap.put(ShotInfo.TAG_ND_FILTER, "ND Filter");
     558        _tagNameMap.put(ShotInfo.TAG_SELF_TIMER_2, "Self Timer 2");
     559        _tagNameMap.put(ShotInfo.TAG_FLASH_OUTPUT, "Flash Output");
     560
     561        _tagNameMap.put(Panorama.TAG_PANORAMA_FRAME_NUMBER, "Panorama Frame Number");
     562        _tagNameMap.put(Panorama.TAG_PANORAMA_DIRECTION, "Panorama Direction");
     563
     564        _tagNameMap.put(AFInfo.TAG_NUM_AF_POINTS, "AF Point Count");
     565        _tagNameMap.put(AFInfo.TAG_VALID_AF_POINTS, "Valid AF Point Count");
     566        _tagNameMap.put(AFInfo.TAG_IMAGE_WIDTH, "Image Width");
     567        _tagNameMap.put(AFInfo.TAG_IMAGE_HEIGHT, "Image Height");
     568        _tagNameMap.put(AFInfo.TAG_AF_IMAGE_WIDTH, "AF Image Width");
     569        _tagNameMap.put(AFInfo.TAG_AF_IMAGE_HEIGHT, "AF Image Height");
     570        _tagNameMap.put(AFInfo.TAG_AF_AREA_WIDTH, "AF Area Width");
     571        _tagNameMap.put(AFInfo.TAG_AF_AREA_HEIGHT, "AF Area Height");
     572        _tagNameMap.put(AFInfo.TAG_AF_AREA_X_POSITIONS, "AF Area X Positions");
     573        _tagNameMap.put(AFInfo.TAG_AF_AREA_Y_POSITIONS, "AF Area Y Positions");
     574        _tagNameMap.put(AFInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus Count");
     575        _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_1, "Primary AF Point 1");
     576        _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_2, "Primary AF Point 2");
     577       
     578//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction");
     579//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS, "Shutter/Auto Exposure-lock Buttons");
     580//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP, "Mirror Lockup");
     581//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL, "Tv/Av And Exposure Level");
     582//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT, "AF-Assist Light");
     583//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE, "Shutter Speed in Av Mode");
     584//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING, "Auto-Exposure Bracketting Sequence/Auto Cancellation");
     585//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC, "Shutter Curtain Sync");
     586//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_STOP, "Lens Auto-Focus Stop Button Function Switch");
     587//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION, "Auto Reduction of Fill Flash");
     588//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN, "Menu Button Return Position");
     589//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION, "SET Button Function When Shooting");
     590//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING, "Sensor Cleaning");
     591
     592        _tagNameMap.put(TAG_THUMBNAIL_IMAGE_VALID_AREA, "Thumbnail Image Valid Area");
     593        _tagNameMap.put(TAG_SERIAL_NUMBER_FORMAT, "Serial Number Format");
     594        _tagNameMap.put(TAG_SUPER_MACRO, "Super Macro");
     595        _tagNameMap.put(TAG_DATE_STAMP_MODE, "Date Stamp Mode");
     596        _tagNameMap.put(TAG_MY_COLORS, "My Colors");
     597        _tagNameMap.put(TAG_FIRMWARE_REVISION, "Firmware Revision");
     598        _tagNameMap.put(TAG_CATEGORIES, "Categories");
     599        _tagNameMap.put(TAG_FACE_DETECT_ARRAY_1, "Face Detect Array 1");
     600        _tagNameMap.put(TAG_FACE_DETECT_ARRAY_2, "Face Detect Array 2");
     601        _tagNameMap.put(TAG_AF_INFO_ARRAY_2, "AF Info Array 2");
     602        _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Image Unique ID");
     603        _tagNameMap.put(TAG_RAW_DATA_OFFSET, "Raw Data Offset");
     604        _tagNameMap.put(TAG_ORIGINAL_DECISION_DATA_OFFSET, "Original Decision Data Offset");
     605        _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_1D_ARRAY, "Custom Functions (1D) Array");
     606        _tagNameMap.put(TAG_PERSONAL_FUNCTIONS_ARRAY, "Personal Functions Array");
     607        _tagNameMap.put(TAG_PERSONAL_FUNCTION_VALUES_ARRAY, "Personal Function Values Array");
     608        _tagNameMap.put(TAG_FILE_INFO_ARRAY, "File Info Array");
     609        _tagNameMap.put(TAG_AF_POINTS_IN_FOCUS_1D, "AF Points in Focus (1D)");
     610        _tagNameMap.put(TAG_LENS_MODEL, "Lens Model");
     611        _tagNameMap.put(TAG_SERIAL_INFO_ARRAY, "Serial Info Array");
     612        _tagNameMap.put(TAG_DUST_REMOVAL_DATA, "Dust Removal Data");
     613        _tagNameMap.put(TAG_CROP_INFO, "Crop Info");
     614        _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_ARRAY_2, "Custom Functions Array 2");
     615        _tagNameMap.put(TAG_ASPECT_INFO_ARRAY, "Aspect Information Array");
     616        _tagNameMap.put(TAG_PROCESSING_INFO_ARRAY, "Processing Information Array");
     617        _tagNameMap.put(TAG_TONE_CURVE_TABLE, "Tone Curve Table");
     618        _tagNameMap.put(TAG_SHARPNESS_TABLE, "Sharpness Table");
     619        _tagNameMap.put(TAG_SHARPNESS_FREQ_TABLE, "Sharpness Frequency Table");
     620        _tagNameMap.put(TAG_WHITE_BALANCE_TABLE, "White Balance Table");
     621        _tagNameMap.put(TAG_COLOR_BALANCE_ARRAY, "Color Balance Array");
     622        _tagNameMap.put(TAG_MEASURED_COLOR_ARRAY, "Measured Color Array");
     623        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
     624        _tagNameMap.put(TAG_CANON_FLAGS_ARRAY, "Canon Flags Array");
     625        _tagNameMap.put(TAG_MODIFIED_INFO_ARRAY, "Modified Information Array");
     626        _tagNameMap.put(TAG_TONE_CURVE_MATCHING, "Tone Curve Matching");
     627        _tagNameMap.put(TAG_WHITE_BALANCE_MATCHING, "White Balance Matching");
     628        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
     629        _tagNameMap.put(TAG_PREVIEW_IMAGE_INFO_ARRAY, "Preview Image Info Array");
     630        _tagNameMap.put(TAG_VRD_OFFSET, "VRD Offset");
     631        _tagNameMap.put(TAG_SENSOR_INFO_ARRAY, "Sensor Information Array");
     632        _tagNameMap.put(TAG_COLOR_DATA_ARRAY_2, "Color Data Array 1");
     633        _tagNameMap.put(TAG_COLOR_INFO_ARRAY_2, "Color Data Array 2");
     634        _tagNameMap.put(TAG_CUSTOM_PICTURE_STYLE_FILE_NAME, "Custom Picture Style File Name");
     635        _tagNameMap.put(TAG_COLOR_INFO_ARRAY, "Color Info Array");
     636        _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_1, "Vignetting Correction Array 1");
     637        _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_2, "Vignetting Correction Array 2");
     638        _tagNameMap.put(TAG_LIGHTING_OPTIMIZER_ARRAY, "Lighting Optimizer Array");
     639        _tagNameMap.put(TAG_LENS_INFO_ARRAY, "Lens Info Array");
     640        _tagNameMap.put(TAG_AMBIANCE_INFO_ARRAY, "Ambiance Info Array");
     641        _tagNameMap.put(TAG_FILTER_INFO_ARRAY, "Filter Info Array");
    398642    }
    399643
     
    403647    }
    404648
     649    @NotNull
    405650    public String getName()
    406651    {
     
    408653    }
    409654
    410     protected HashMap getTagNameMap()
     655    @NotNull
     656    protected HashMap<Integer, String> getTagNameMap()
    411657    {
    412658        return _tagNameMap;
    413659    }
    414660
    415     /**
    416      * We need special handling for selected tags.
    417      * @param tagType
    418      * @param ints
    419      */
    420     public void setIntArray(int tagType, int[] ints)
    421     {
    422         if (tagType == TAG_CANON_CAMERA_STATE_1) {
    423             // this single tag has multiple values within
    424             int subTagTypeBase = 0xC100;
    425             // we intentionally skip the first array member
    426             for (int i = 1; i < ints.length; i++) {
    427                 setInt(subTagTypeBase + i, ints[i]);
    428             }
    429         } else if (tagType == TAG_CANON_CAMERA_STATE_2) {
    430             // this single tag has multiple values within
    431             int subTagTypeBase = 0xC200;
    432             // we intentionally skip the first array member
    433             for (int i = 1; i < ints.length; i++) {
    434                 setInt(subTagTypeBase + i, ints[i]);
    435             }
    436         } if (tagType == TAG_CANON_CUSTOM_FUNCTIONS) {
    437             // this single tag has multiple values within
    438             int subTagTypeBase = 0xC300;
    439             // we intentionally skip the first array member
    440             for (int i = 1; i < ints.length; i++) {
    441                 setInt(subTagTypeBase + i + 1, ints[i] & 0x0F);
    442             }
    443         } else {
    444             // no special handling...
    445             super.setIntArray(tagType, ints);
     661    @Override
     662    public void setIntArray(int tagType, @NotNull int[] ints)
     663    {
     664        // TODO is there some way to drop out 'null' or 'zero' values that are present in the array to reduce the noise?
     665       
     666        // Certain Canon tags contain arrays of values that we split into 'fake' tags as each
     667        // index in the array has its own meaning and decoding.
     668        // Pick those tags out here and throw away the original array.
     669        // Otherwise just add as usual.
     670        switch (tagType) {
     671            case TAG_CAMERA_SETTINGS_ARRAY:
     672                for (int i = 0; i < ints.length; i++)
     673                    setInt(CameraSettings.OFFSET + i, ints[i]);
     674                break;
     675            case TAG_FOCAL_LENGTH_ARRAY:
     676                for (int i = 0; i < ints.length; i++)
     677                    setInt(FocalLength.OFFSET + i, ints[i]);
     678                break;
     679            case TAG_SHOT_INFO_ARRAY:
     680                for (int i = 0; i < ints.length; i++)
     681                    setInt(ShotInfo.OFFSET + i, ints[i]);
     682                break;
     683            case TAG_PANORAMA_ARRAY:
     684                for (int i = 0; i < ints.length; i++)
     685                    setInt(Panorama.OFFSET + i, ints[i]);
     686                break;
     687            // TODO the interpretation of the custom functions tag depends upon the camera model
     688//            case TAG_CANON_CUSTOM_FUNCTIONS_ARRAY:
     689//                int subTagTypeBase = 0xC300;
     690//                // we intentionally skip the first array member
     691//                for (int i = 1; i < ints.length; i++)
     692//                    setInt(subTagTypeBase + i + 1, ints[i] & 0x0F);
     693//                break;
     694            case TAG_AF_INFO_ARRAY:
     695                for (int i = 0; i < ints.length; i++)
     696                    setInt(AFInfo.OFFSET + i, ints[i]);
     697                break;
     698            default:
     699                // no special handling...
     700                super.setIntArray(tagType, ints);
     701                break;
    446702        }
    447703    }
  • trunk/src/com/drew/metadata/exif/CasioType1MakernoteDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
    24  *
     28 * Provides human-readable string representations of tag values stored in a <code>CasioType1MakernoteDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class CasioType1MakernoteDescriptor extends TagDescriptor
     32public class CasioType1MakernoteDescriptor extends TagDescriptor<CasioType1MakernoteDirectory>
    2733{
    28     public CasioType1MakernoteDescriptor(Directory directory)
     34    public CasioType1MakernoteDescriptor(@NotNull CasioType1MakernoteDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
     
    5966                return getCcdSensitivityDescription();
    6067            default:
    61                 return _directory.getString(tagType);
    62         }
    63     }
    64 
    65     public String getCcdSensitivityDescription() throws MetadataException
    66     {
    67         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY)) return null;
    68         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY);
     68                return super.getDescription(tagType);
     69        }
     70    }
     71
     72    @Nullable
     73    public String getCcdSensitivityDescription()
     74    {
     75        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY);
     76
     77        if (value == null)
     78            return null;
     79
    6980        switch (value) {
    7081            // these four for QV3000
     
    8798    }
    8899
    89     public String getSaturationDescription() throws MetadataException
    90     {
    91         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION)) return null;
    92         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION);
     100    @Nullable
     101    public String getSaturationDescription()
     102    {
     103        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION);
     104
     105        if (value == null)
     106            return null;
     107
    93108        switch (value) {
    94109            case 0:
     
    103118    }
    104119
    105     public String getContrastDescription() throws MetadataException
    106     {
    107         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST)) return null;
    108         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST);
     120    @Nullable
     121    public String getContrastDescription()
     122    {
     123        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST);
     124
     125        if (value == null)
     126            return null;
     127
    109128        switch (value) {
    110129            case 0:
     
    119138    }
    120139
    121     public String getSharpnessDescription() throws MetadataException
    122     {
    123         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS)) return null;
    124         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS);
     140    @Nullable
     141    public String getSharpnessDescription()
     142    {
     143        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS);
     144
     145        if (value == null)
     146            return null;
     147
    125148        switch (value) {
    126149            case 0:
     
    135158    }
    136159
    137     public String getDigitalZoomDescription() throws MetadataException
    138     {
    139         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM)) return null;
    140         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM);
     160    @Nullable
     161    public String getDigitalZoomDescription()
     162    {
     163        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM);
     164
     165        if (value == null)
     166            return null;
     167
    141168        switch (value) {
    142169            case 0x10000:
     
    153180    }
    154181
    155     public String getWhiteBalanceDescription() throws MetadataException
    156     {
    157         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE)) return null;
    158         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE);
     182    @Nullable
     183    public String getWhiteBalanceDescription()
     184    {
     185        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE);
     186
     187        if (value == null)
     188            return null;
     189
    159190        switch (value) {
    160191            case 1:
     
    165196                return "Daylight";
    166197            case 4:
    167                 return "Flourescent";
     198                return "Florescent";
    168199            case 5:
    169200                return "Shade";
     
    175206    }
    176207
    177     public String getObjectDistanceDescription() throws MetadataException
    178     {
    179         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE)) return null;
    180         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE);
     208    @Nullable
     209    public String getObjectDistanceDescription()
     210    {
     211        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE);
     212
     213        if (value == null)
     214            return null;
     215
    181216        return value + " mm";
    182217    }
    183218
    184     public String getFlashIntensityDescription() throws MetadataException
    185     {
    186         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY)) return null;
    187         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY);
     219    @Nullable
     220    public String getFlashIntensityDescription()
     221    {
     222        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY);
     223
     224        if (value == null)
     225            return null;
     226
    188227        switch (value) {
    189228            case 11:
     
    198237    }
    199238
    200     public String getFlashModeDescription() throws MetadataException
    201     {
    202         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE)) return null;
    203         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE);
     239    @Nullable
     240    public String getFlashModeDescription()
     241    {
     242        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE);
     243
     244        if (value == null)
     245            return null;
     246
    204247        switch (value) {
    205248            case 1:
     
    218261    }
    219262
    220     public String getFocusingModeDescription() throws MetadataException
    221     {
    222         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE)) return null;
    223         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE);
     263    @Nullable
     264    public String getFocusingModeDescription()
     265    {
     266        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE);
     267
     268        if (value == null)
     269            return null;
     270
    224271        switch (value) {
    225272            case 2:
     
    236283    }
    237284
    238     public String getQualityDescription() throws MetadataException
    239     {
    240         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY)) return null;
    241         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY);
     285    @Nullable
     286    public String getQualityDescription()
     287    {
     288        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY);
     289
     290        if (value == null)
     291            return null;
     292
    242293        switch (value) {
    243294            case 1:
     
    252303    }
    253304
    254     public String getRecordingModeDescription() throws MetadataException
    255     {
    256         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE)) return null;
    257         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE);
     305    @Nullable
     306    public String getRecordingModeDescription()
     307    {
     308        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE);
     309
     310        if (value == null)
     311            return null;
     312
    258313        switch (value) {
    259314            case 1:
  • trunk/src/com/drew/metadata/exif/CasioType1MakernoteDirectory.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 27-Nov-2002 10:10:47 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Casio (type 1) cameras.
     30 *
    2431 * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment.
    2532 * Makernote data begins immediately (no header).
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2635 */
    2736public class CasioType1MakernoteDirectory extends Directory
     
    4857    public static final int TAG_CASIO_CCD_SENSITIVITY = 0x0014;
    4958
    50     protected static final HashMap<Integer, String> tagNameMap = new HashMap<Integer, String>();
     59    @NotNull
     60    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    5161
    5262    static
    5363    {
    54         tagNameMap.put(new Integer(TAG_CASIO_CCD_SENSITIVITY), "CCD Sensitivity");
    55         tagNameMap.put(new Integer(TAG_CASIO_CONTRAST), "Contrast");
    56         tagNameMap.put(new Integer(TAG_CASIO_DIGITAL_ZOOM), "Digital Zoom");
    57         tagNameMap.put(new Integer(TAG_CASIO_FLASH_INTENSITY), "Flash Intensity");
    58         tagNameMap.put(new Integer(TAG_CASIO_FLASH_MODE), "Flash Mode");
    59         tagNameMap.put(new Integer(TAG_CASIO_FOCUSING_MODE), "Focussing Mode");
    60         tagNameMap.put(new Integer(TAG_CASIO_OBJECT_DISTANCE), "Object Distance");
    61         tagNameMap.put(new Integer(TAG_CASIO_QUALITY), "Quality");
    62         tagNameMap.put(new Integer(TAG_CASIO_RECORDING_MODE), "Recording Mode");
    63         tagNameMap.put(new Integer(TAG_CASIO_SATURATION), "Saturation");
    64         tagNameMap.put(new Integer(TAG_CASIO_SHARPNESS), "Sharpness");
    65         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_1), "Makernote Unknown 1");
    66         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_2), "Makernote Unknown 2");
    67         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_3), "Makernote Unknown 3");
    68         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_4), "Makernote Unknown 4");
    69         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_5), "Makernote Unknown 5");
    70         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_6), "Makernote Unknown 6");
    71         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_7), "Makernote Unknown 7");
    72         tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_8), "Makernote Unknown 8");
    73         tagNameMap.put(new Integer(TAG_CASIO_WHITE_BALANCE), "White Balance");
     64        _tagNameMap.put(TAG_CASIO_CCD_SENSITIVITY, "CCD Sensitivity");
     65        _tagNameMap.put(TAG_CASIO_CONTRAST, "Contrast");
     66        _tagNameMap.put(TAG_CASIO_DIGITAL_ZOOM, "Digital Zoom");
     67        _tagNameMap.put(TAG_CASIO_FLASH_INTENSITY, "Flash Intensity");
     68        _tagNameMap.put(TAG_CASIO_FLASH_MODE, "Flash Mode");
     69        _tagNameMap.put(TAG_CASIO_FOCUSING_MODE, "Focusing Mode");
     70        _tagNameMap.put(TAG_CASIO_OBJECT_DISTANCE, "Object Distance");
     71        _tagNameMap.put(TAG_CASIO_QUALITY, "Quality");
     72        _tagNameMap.put(TAG_CASIO_RECORDING_MODE, "Recording Mode");
     73        _tagNameMap.put(TAG_CASIO_SATURATION, "Saturation");
     74        _tagNameMap.put(TAG_CASIO_SHARPNESS, "Sharpness");
     75        _tagNameMap.put(TAG_CASIO_UNKNOWN_1, "Makernote Unknown 1");
     76        _tagNameMap.put(TAG_CASIO_UNKNOWN_2, "Makernote Unknown 2");
     77        _tagNameMap.put(TAG_CASIO_UNKNOWN_3, "Makernote Unknown 3");
     78        _tagNameMap.put(TAG_CASIO_UNKNOWN_4, "Makernote Unknown 4");
     79        _tagNameMap.put(TAG_CASIO_UNKNOWN_5, "Makernote Unknown 5");
     80        _tagNameMap.put(TAG_CASIO_UNKNOWN_6, "Makernote Unknown 6");
     81        _tagNameMap.put(TAG_CASIO_UNKNOWN_7, "Makernote Unknown 7");
     82        _tagNameMap.put(TAG_CASIO_UNKNOWN_8, "Makernote Unknown 8");
     83        _tagNameMap.put(TAG_CASIO_WHITE_BALANCE, "White Balance");
    7484    }
    7585
     
    7989    }
    8090
     91    @NotNull
    8192    public String getName()
    8293    {
     
    8495    }
    8596
    86     protected HashMap getTagNameMap()
     97    @NotNull
     98    protected HashMap<Integer, String> getTagNameMap()
    8799    {
    88         return tagNameMap;
     100        return _tagNameMap;
    89101    }
    90102}
  • trunk/src/com/drew/metadata/exif/CasioType2MakernoteDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
    24  *
     28 * Provides human-readable string representations of tag values stored in a <code>CasioType2MakernoteDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class CasioType2MakernoteDescriptor extends TagDescriptor
     32public class CasioType2MakernoteDescriptor extends TagDescriptor<CasioType2MakernoteDirectory>
    2733{
    28     public CasioType2MakernoteDescriptor(Directory directory)
     34    public CasioType2MakernoteDescriptor(@NotNull CasioType2MakernoteDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
     
    9198                return getFilterDescription();
    9299            default:
    93                 return _directory.getString(tagType);
    94         }
    95     }
    96 
    97     public String getFilterDescription() throws MetadataException
    98     {
    99         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER)) return null;
    100         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER);
    101         switch (value) {
    102             case 0:
    103                 return "Off";
    104             default:
    105                 return "Unknown (" + value + ")";
    106         }
    107     }
    108 
    109     public String getEnhancementDescription() throws MetadataException
    110     {
    111         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT)) return null;
    112         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT);
    113         switch (value) {
    114             case 0:
    115                 return "Off";
    116             default:
    117                 return "Unknown (" + value + ")";
    118         }
    119     }
    120 
    121     public String getColourModeDescription() throws MetadataException
    122     {
    123         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE)) return null;
    124         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE);
    125         switch (value) {
    126             case 0:
    127                 return "Off";
    128             default:
    129                 return "Unknown (" + value + ")";
    130         }
    131     }
    132 
    133     public String getCcdIsoSensitivityDescription() throws MetadataException
    134     {
    135         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY)) return null;
    136         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY);
     100                return super.getDescription(tagType);
     101        }
     102    }
     103
     104    @Nullable
     105    public String getFilterDescription()
     106    {
     107        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER);
     108        if (value==null)
     109            return null;
     110        switch (value) {
     111            case 0:
     112                return "Off";
     113            default:
     114                return "Unknown (" + value + ")";
     115        }
     116    }
     117
     118    @Nullable
     119    public String getEnhancementDescription()
     120    {
     121        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT);
     122        if (value==null)
     123            return null;
     124        switch (value) {
     125            case 0:
     126                return "Off";
     127            default:
     128                return "Unknown (" + value + ")";
     129        }
     130    }
     131
     132    @Nullable
     133    public String getColourModeDescription()
     134    {
     135        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE);
     136        if (value==null)
     137            return null;
     138        switch (value) {
     139            case 0:
     140                return "Off";
     141            default:
     142                return "Unknown (" + value + ")";
     143        }
     144    }
     145
     146    @Nullable
     147    public String getCcdIsoSensitivityDescription()
     148    {
     149        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY);
     150        if (value==null)
     151            return null;
    137152        switch (value) {
    138153            case 0:
     
    145160    }
    146161
    147     public String getBestShotModeDescription() throws MetadataException
    148     {
    149         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE)) return null;
    150         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE);
    151         switch (value) {
    152             default:
    153                 return "Unknown (" + value + ")";
    154         }
    155     }
    156 
     162    @Nullable
     163    public String getBestShotModeDescription()
     164    {
     165        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE);
     166        if (value==null)
     167            return null;
     168        switch (value) {
     169            default:
     170                return "Unknown (" + value + ")";
     171        }
     172    }
     173
     174    @Nullable
    157175    public String getTimeZoneDescription()
    158176    {
    159         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_TIME_ZONE)) return null;
    160177        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_TIME_ZONE);
    161178    }
    162179
    163     public String getFocusMode2Description() throws MetadataException
    164     {
    165         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2)) return null;
    166         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2);
     180    @Nullable
     181    public String getFocusMode2Description()
     182    {
     183        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2);
     184        if (value==null)
     185            return null;
    167186        switch (value) {
    168187            case 1:
     
    175194    }
    176195
    177     public String getQualityDescription() throws MetadataException
    178     {
    179         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY)) return null;
    180         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY);
     196    @Nullable
     197    public String getQualityDescription()
     198    {
     199        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY);
     200        if (value==null)
     201            return null;
    181202        switch (value) {
    182203            case 3:
     
    187208    }
    188209
    189     public String getSelfTimerDescription() throws MetadataException
    190     {
    191         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER)) return null;
    192         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER);
    193         switch (value) {
    194             case 1:
    195                 return "Off";
    196             default:
    197                 return "Unknown (" + value + ")";
    198         }
    199     }
    200 
    201     public String getRecordModeDescription() throws MetadataException
    202     {
    203         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE)) return null;
    204         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE);
     210    @Nullable
     211    public String getSelfTimerDescription()
     212    {
     213        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER);
     214        if (value==null)
     215            return null;
     216        switch (value) {
     217            case 1:
     218                return "Off";
     219            default:
     220                return "Unknown (" + value + ")";
     221        }
     222    }
     223
     224    @Nullable
     225    public String getRecordModeDescription()
     226    {
     227        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE);
     228        if (value==null)
     229            return null;
    205230        switch (value) {
    206231            case 2:
     
    211236    }
    212237
    213     public String getFlashDistanceDescription() throws MetadataException
    214     {
    215         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE)) return null;
    216         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE);
    217         switch (value) {
    218             case 0:
    219                 return "Off";
    220             default:
    221                 return "Unknown (" + value + ")";
    222         }
    223     }
    224 
    225     public String getObjectDistanceDescription() throws MetadataException
    226     {
    227         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE)) return null;
    228         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE);
     238    @Nullable
     239    public String getFlashDistanceDescription()
     240    {
     241        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE);
     242        if (value==null)
     243            return null;
     244        switch (value) {
     245            case 0:
     246                return "Off";
     247            default:
     248                return "Unknown (" + value + ")";
     249        }
     250    }
     251
     252    @Nullable
     253    public String getObjectDistanceDescription()
     254    {
     255        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE);
     256        if (value==null)
     257            return null;
    229258        return Integer.toString(value) + " mm";
    230259    }
    231260
    232     public String getWhiteBalance2Description() throws MetadataException
    233     {
    234         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2)) return null;
    235         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2);
     261    @Nullable
     262    public String getWhiteBalance2Description()
     263    {
     264        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2);
     265        if (value==null)
     266            return null;
    236267        switch (value) {
    237268            case 0:
     
    248279    }
    249280
     281    @Nullable
    250282    public String getWhiteBalanceBiasDescription()
    251283    {
    252         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS)) return null;
    253284        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS);
    254285    }
    255286
    256     public String getCasioPreviewThumbnailDescription() throws MetadataException
    257     {
    258         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL)) return null;
     287    @Nullable
     288    public String getCasioPreviewThumbnailDescription()
     289    {
    259290        final byte[] bytes = _directory.getByteArray(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL);
     291        if (bytes==null)
     292            return null;
    260293        return "<" + bytes.length + " bytes of image data>";
    261294    }
    262295
     296    @Nullable
    263297    public String getPrintImageMatchingInfoDescription()
    264298    {
    265299        // TODO research PIM specification http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
    266         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO)) return null;
    267300        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO);
    268301    }
    269302
    270     public String getSharpnessDescription() throws MetadataException
    271     {
    272         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS)) return null;
    273         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS);
     303    @Nullable
     304    public String getSharpnessDescription()
     305    {
     306        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS);
     307        if (value==null)
     308            return null;
    274309        switch (value) {
    275310            case 0:
     
    284319    }
    285320
    286     public String getContrastDescription() throws MetadataException
    287     {
    288         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST)) return null;
    289         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST);
     321    @Nullable
     322    public String getContrastDescription()
     323    {
     324        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST);
     325        if (value==null)
     326            return null;
    290327        switch (value) {
    291328            case 0:
     
    300337    }
    301338
    302     public String getSaturationDescription() throws MetadataException
    303     {
    304         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION)) return null;
    305         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION);
     339    @Nullable
     340    public String getSaturationDescription()
     341    {
     342        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION);
     343        if (value==null)
     344            return null;
    306345        switch (value) {
    307346            case 0:
     
    316355    }
    317356
    318     public String getFocalLengthDescription() throws MetadataException
    319     {
    320         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH)) return null;
    321         double value = _directory.getDouble(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH);
     357    @Nullable
     358    public String getFocalLengthDescription()
     359    {
     360        Double value = _directory.getDoubleObject(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH);
     361        if (value==null)
     362            return null;
    322363        return Double.toString(value / 10d) + " mm";
    323364    }
    324365
    325     public String getWhiteBalance1Description() throws MetadataException
    326     {
    327         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1)) return null;
    328         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1);
     366    @Nullable
     367    public String getWhiteBalance1Description()
     368    {
     369        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1);
     370        if (value==null)
     371            return null;
    329372        switch (value) {
    330373            case 0:
     
    337380                return "Tungsten";
    338381            case 4:
    339                 return "Flourescent";
     382                return "Florescent";
    340383            case 5:
    341384                return "Manual";
     
    345388    }
    346389
    347     public String getIsoSensitivityDescription() throws MetadataException
    348     {
    349         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY)) return null;
    350         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY);
     390    @Nullable
     391    public String getIsoSensitivityDescription()
     392    {
     393        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY);
     394        if (value==null)
     395            return null;
    351396        switch (value) {
    352397            case 3:
     
    363408    }
    364409
    365     public String getFocusMode1Description() throws MetadataException
    366     {
    367         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1)) return null;
    368         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1);
     410    @Nullable
     411    public String getFocusMode1Description()
     412    {
     413        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1);
     414        if (value==null)
     415            return null;
    369416        switch (value) {
    370417            case 0:
     
    377424    }
    378425
    379     public String getImageSizeDescription() throws MetadataException
    380     {
    381         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE)) return null;
    382         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE);
     426    @Nullable
     427    public String getImageSizeDescription()
     428    {
     429        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE);
     430        if (value==null)
     431            return null;
    383432        switch (value) {
    384433            case 0:  return "640 x 480 pixels";
     
    393442    }
    394443
    395     public String getQualityModeDescription() throws MetadataException
    396     {
    397         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE)) return null;
    398         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE);
     444    @Nullable
     445    public String getQualityModeDescription()
     446    {
     447        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE);
     448        if (value==null)
     449            return null;
    399450        switch (value) {
    400451            case 1:
     
    407458    }
    408459
     460    @Nullable
    409461    public String getThumbnailOffsetDescription()
    410462    {
    411         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_OFFSET)) return null;
    412463        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_OFFSET);
    413464    }
    414465
    415     public String getThumbnailSizeDescription() throws MetadataException
    416     {
    417         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE)) return null;
    418         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE);
     466    @Nullable
     467    public String getThumbnailSizeDescription()
     468    {
     469        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE);
     470        if (value==null)
     471            return null;
    419472        return Integer.toString(value) + " bytes";
    420473    }
    421474
    422     public String getThumbnailDimensionsDescription() throws MetadataException
    423     {
    424         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS)) return null;
     475    @Nullable
     476    public String getThumbnailDimensionsDescription()
     477    {
    425478        int[] dimensions = _directory.getIntArray(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS);
    426         if (dimensions.length!=2)
     479        if (dimensions==null || dimensions.length!=2)
    427480            return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS);
    428481        return dimensions[0] + " x " + dimensions[1] + " pixels";
  • trunk/src/com/drew/metadata/exif/CasioType2MakernoteDirectory.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 27-Nov-2002 10:10:47 using IntelliJ IDEA.
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Casio (type 2) cameras.
     30 *
    2431 * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment.
    2532 * Makernote data begins after a 6-byte header: "QVC\x00\x00\x00"
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2635 */
    2736public class CasioType2MakernoteDirectory extends Directory
     
    167176    public static final int TAG_CASIO_TYPE2_FILTER = 0x3017;
    168177
    169     protected static final HashMap tagNameMap = new HashMap();
     178    @NotNull
     179    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    170180
    171181    static
    172182    {
    173         // TODO add names
    174         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS), "Thumbnail Dimensions");
    175         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_THUMBNAIL_SIZE), "Thumbnail Size");
    176         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_THUMBNAIL_OFFSET), "Thumbnail Offset");
    177         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_QUALITY_MODE), "Quality Mode");
    178         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_IMAGE_SIZE), "Image Size");
    179         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FOCUS_MODE_1), "Focus Mode");
    180         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_ISO_SENSITIVITY), "ISO Sensitivity");
    181         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_WHITE_BALANCE_1), "White Balance");
    182         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FOCAL_LENGTH), "Focal Length");
    183         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_SATURATION), "Saturation");
    184         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_CONTRAST), "Contrast");
    185         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_SHARPNESS), "Sharpness");
    186         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");
    187         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL), "Casio Preview Thumbnail");
    188         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS), "White Balance Bias");
    189         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_WHITE_BALANCE_2), "White Balance");
    190         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_OBJECT_DISTANCE), "Object Distance");
    191         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FLASH_DISTANCE), "Flash Distance");
    192         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_RECORD_MODE), "Record Mode");
    193         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_SELF_TIMER), "Self Timer");
    194         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_QUALITY), "Quality");
    195         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FOCUS_MODE_2), "Focus Mode");
    196         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_TIME_ZONE), "Time Zone");
    197         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_BESTSHOT_MODE), "BestShot Mode");
    198         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY), "CCD ISO Sensitivity");
    199         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_COLOUR_MODE), "Colour Mode");
    200         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_ENHANCEMENT), "Enhancement");
    201         tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FILTER), "Filter");
     183        // TODO add missing names
     184        _tagNameMap.put(TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS, "Thumbnail Dimensions");
     185        _tagNameMap.put(TAG_CASIO_TYPE2_THUMBNAIL_SIZE, "Thumbnail Size");
     186        _tagNameMap.put(TAG_CASIO_TYPE2_THUMBNAIL_OFFSET, "Thumbnail Offset");
     187        _tagNameMap.put(TAG_CASIO_TYPE2_QUALITY_MODE, "Quality Mode");
     188        _tagNameMap.put(TAG_CASIO_TYPE2_IMAGE_SIZE, "Image Size");
     189        _tagNameMap.put(TAG_CASIO_TYPE2_FOCUS_MODE_1, "Focus Mode");
     190        _tagNameMap.put(TAG_CASIO_TYPE2_ISO_SENSITIVITY, "ISO Sensitivity");
     191        _tagNameMap.put(TAG_CASIO_TYPE2_WHITE_BALANCE_1, "White Balance");
     192        _tagNameMap.put(TAG_CASIO_TYPE2_FOCAL_LENGTH, "Focal Length");
     193        _tagNameMap.put(TAG_CASIO_TYPE2_SATURATION, "Saturation");
     194        _tagNameMap.put(TAG_CASIO_TYPE2_CONTRAST, "Contrast");
     195        _tagNameMap.put(TAG_CASIO_TYPE2_SHARPNESS, "Sharpness");
     196        _tagNameMap.put(TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
     197        _tagNameMap.put(TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL, "Casio Preview Thumbnail");
     198        _tagNameMap.put(TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS, "White Balance Bias");
     199        _tagNameMap.put(TAG_CASIO_TYPE2_WHITE_BALANCE_2, "White Balance");
     200        _tagNameMap.put(TAG_CASIO_TYPE2_OBJECT_DISTANCE, "Object Distance");
     201        _tagNameMap.put(TAG_CASIO_TYPE2_FLASH_DISTANCE, "Flash Distance");
     202        _tagNameMap.put(TAG_CASIO_TYPE2_RECORD_MODE, "Record Mode");
     203        _tagNameMap.put(TAG_CASIO_TYPE2_SELF_TIMER, "Self Timer");
     204        _tagNameMap.put(TAG_CASIO_TYPE2_QUALITY, "Quality");
     205        _tagNameMap.put(TAG_CASIO_TYPE2_FOCUS_MODE_2, "Focus Mode");
     206        _tagNameMap.put(TAG_CASIO_TYPE2_TIME_ZONE, "Time Zone");
     207        _tagNameMap.put(TAG_CASIO_TYPE2_BESTSHOT_MODE, "BestShot Mode");
     208        _tagNameMap.put(TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY, "CCD ISO Sensitivity");
     209        _tagNameMap.put(TAG_CASIO_TYPE2_COLOUR_MODE, "Colour Mode");
     210        _tagNameMap.put(TAG_CASIO_TYPE2_ENHANCEMENT, "Enhancement");
     211        _tagNameMap.put(TAG_CASIO_TYPE2_FILTER, "Filter");
    202212    }
    203213
     
    207217    }
    208218
     219    @NotNull
    209220    public String getName()
    210221    {
     
    212223    }
    213224
    214     protected HashMap getTagNameMap()
    215     {
    216         return tagNameMap;
     225    @NotNull
     226    protected HashMap<Integer, String> getTagNameMap()
     227    {
     228        return _tagNameMap;
    217229    }
    218230}
  • trunk/src/com/drew/metadata/exif/DataFormat.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1420 */
    1521package com.drew.metadata.exif;
    1622
     23import com.drew.lang.annotations.NotNull;
    1724import com.drew.metadata.MetadataException;
    1825
    1926/**
    2027 * An enumeration of data formats used in the TIFF IFDs.
     28 *
     29 * @author Drew Noakes http://drewnoakes.com
    2130 */
    2231public class DataFormat
    2332{
    24     public static final DataFormat BYTE = new DataFormat("BYTE", 1);
    25     public static final DataFormat STRING = new DataFormat("STRING", 2);
    26     public static final DataFormat USHORT = new DataFormat("USHORT", 3);
    27     public static final DataFormat ULONG = new DataFormat("ULONG", 4);
    28     public static final DataFormat URATIONAL = new DataFormat("URATIONAL", 5);
    29     public static final DataFormat SBYTE = new DataFormat("SBYTE", 6);
    30     public static final DataFormat UNDEFINED = new DataFormat("UNDEFINED", 7);
    31     public static final DataFormat SSHORT = new DataFormat("SSHORT", 8);
    32     public static final DataFormat SLONG = new DataFormat("SLONG", 9);
    33     public static final DataFormat SRATIONAL = new DataFormat("SRATIONAL", 10);
    34     public static final DataFormat SINGLE = new DataFormat("SINGLE", 11);
    35     public static final DataFormat DOUBLE = new DataFormat("DOUBLE", 12);
     33    @NotNull public static final DataFormat BYTE = new DataFormat("BYTE", 1);
     34    @NotNull public static final DataFormat STRING = new DataFormat("STRING", 2);
     35    @NotNull public static final DataFormat USHORT = new DataFormat("USHORT", 3);
     36    @NotNull public static final DataFormat ULONG = new DataFormat("ULONG", 4);
     37    @NotNull public static final DataFormat URATIONAL = new DataFormat("URATIONAL", 5);
     38    @NotNull public static final DataFormat SBYTE = new DataFormat("SBYTE", 6);
     39    @NotNull public static final DataFormat UNDEFINED = new DataFormat("UNDEFINED", 7);
     40    @NotNull public static final DataFormat SSHORT = new DataFormat("SSHORT", 8);
     41    @NotNull public static final DataFormat SLONG = new DataFormat("SLONG", 9);
     42    @NotNull public static final DataFormat SRATIONAL = new DataFormat("SRATIONAL", 10);
     43    @NotNull public static final DataFormat SINGLE = new DataFormat("SINGLE", 11);
     44    @NotNull public static final DataFormat DOUBLE = new DataFormat("DOUBLE", 12);
    3645
    37     private final String myName;
    38     private final int value;
     46    @NotNull private final String _name;
     47    private final int _value;
    3948
     49    @NotNull
    4050    public static DataFormat fromValue(int value) throws MetadataException
    4151    {
     
    5969    }
    6070
    61     private DataFormat(String name, int value)
     71    private DataFormat(@NotNull String name, int value)
    6272    {
    63         myName = name;
    64         this.value = value;
     73        _name = name;
     74        _value = value;
    6575    }
    6676
    6777    public int getValue()
    6878    {
    69         return value;
     79        return _value;
    7080    }
    7181
     82    @NotNull
    7283    public String toString()
    7384    {
    74         return myName;
     85        return _name;
    7586    }
    7687}
  • trunk/src/com/drew/metadata/exif/ExifInteropDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 12-Nov-2002 22:27:34 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
     28 * Provides human-readable string representations of tag values stored in a <code>ExifInteropDirectory</code>.
    2429 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class ExifInteropDescriptor extends TagDescriptor
     32public class ExifInteropDescriptor extends TagDescriptor<ExifInteropDirectory>
    2733{
    28     public ExifInteropDescriptor(Directory directory)
     34    public ExifInteropDescriptor(@NotNull ExifInteropDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
     
    3946                return getInteropVersionDescription();
    4047            default:
    41                 return _directory.getString(tagType);
     48                return super.getDescription(tagType);
    4249        }
    4350    }
    4451
    45     public String getInteropVersionDescription() throws MetadataException
     52    @Nullable
     53    public String getInteropVersionDescription()
    4654    {
    47         if (!_directory.containsTag(ExifInteropDirectory.TAG_INTEROP_VERSION)) return null;
    4855        int[] ints = _directory.getIntArray(ExifInteropDirectory.TAG_INTEROP_VERSION);
    49         return ExifDescriptor.convertBytesToVersionString(ints);
     56        return convertBytesToVersionString(ints, 2);
    5057    }
    5158
     59    @Nullable
    5260    public String getInteropIndexDescription()
    5361    {
    54         if (!_directory.containsTag(ExifInteropDirectory.TAG_INTEROP_INDEX)) return null;
    55         String interopIndex = _directory.getString(ExifInteropDirectory.TAG_INTEROP_INDEX).trim();
    56         if ("R98".equalsIgnoreCase(interopIndex)) {
    57             return "Recommended Exif Interoperability Rules (ExifR98)";
    58         } else {
    59             return "Unknown (" + interopIndex + ")";
    60         }
     62        String value = _directory.getString(ExifInteropDirectory.TAG_INTEROP_INDEX);
     63
     64        if (value==null)
     65            return null;
     66
     67        return "R98".equalsIgnoreCase(value.trim())
     68                ? "Recommended Exif Interoperability Rules (ExifR98)"
     69                : "Unknown (" + value + ")";
    6170    }
    6271}
  • trunk/src/com/drew/metadata/exif/ExifInteropDirectory.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 26-Nov-2002 10:58:13 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes Exif interoperability tags.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class ExifInteropDirectory extends Directory
     
    3239    public static final int TAG_RELATED_IMAGE_LENGTH = 0x1002;
    3340
    34     protected static final HashMap tagNameMap;
     41    @NotNull
     42    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    3543
    3644    static
    3745    {
    38         tagNameMap = new HashMap();
    39         tagNameMap.put(new Integer(TAG_INTEROP_INDEX), "Interoperability Index");
    40         tagNameMap.put(new Integer(TAG_INTEROP_VERSION), "Interoperability Version");
    41         tagNameMap.put(new Integer(TAG_RELATED_IMAGE_FILE_FORMAT), "Related Image File Format");
    42         tagNameMap.put(new Integer(TAG_RELATED_IMAGE_WIDTH), "Related Image Width");
    43         tagNameMap.put(new Integer(TAG_RELATED_IMAGE_LENGTH), "Related Image Length");
     46        _tagNameMap.put(TAG_INTEROP_INDEX, "Interoperability Index");
     47        _tagNameMap.put(TAG_INTEROP_VERSION, "Interoperability Version");
     48        _tagNameMap.put(TAG_RELATED_IMAGE_FILE_FORMAT, "Related Image File Format");
     49        _tagNameMap.put(TAG_RELATED_IMAGE_WIDTH, "Related Image Width");
     50        _tagNameMap.put(TAG_RELATED_IMAGE_LENGTH, "Related Image Length");
    4451    }
    4552
     
    4956    }
    5057
     58    @NotNull
    5159    public String getName()
    5260    {
     
    5462    }
    5563
    56     protected HashMap getTagNameMap()
     64    @NotNull
     65    protected HashMap<Integer, String> getTagNameMap()
    5766    {
    58         return tagNameMap;
     67        return _tagNameMap;
    5968    }
    6069}
  • trunk/src/com/drew/metadata/exif/ExifReader.java

    r4231 r6127  
    11/*
    2  * EXIFExtractor.java
     2 * Copyright 2002-2012 Drew Noakes
    33 *
    4  * This class based upon code from Jhead, a C program for extracting and
    5  * manipulating the Exif data within files written by Matthias Wandel.
    6  *   http://www.sentex.net/~mwandel/jhead/
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    77 *
    8  * Jhead is public domain software - that is, you can do whatever you want
    9  * with it, and include it software that is licensed under the GNU or the
    10  * BSD license, or whatever other licence you choose, including proprietary
    11  * closed source licenses.  Similarly, I release this Java version under the
    12  * same license, though I do ask that you leave this header in tact.
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    139 *
    14  * If you make modifications to this code that you think would benefit the
    15  * wider community, please send me a copy and I'll post it on my site.  Unlike
    16  * Jhead, this code (as it stands) only supports reading of Exif data - no
    17  * manipulation, and no thumbnail stuff.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
    1815 *
    19  * If you make use of this code, I'd appreciate hearing about it.
    20  *   drew.noakes@drewnoakes.com
    21  * Latest version of this software kept at
    22  *   http://drewnoakes.com/
     16 * More information about this project is available at:
    2317 *
    24  * Created on 28 April 2002, 23:54
    25  * Modified 04 Aug 2002
    26  * - Renamed constants to be inline with changes to ExifTagValues interface
    27  * - Substituted usage of JDK 1.4 features (java.nio package)
    28  * Modified 29 Oct 2002 (v1.2)
    29  * - Proper traversing of Exif file structure and complete refactor & tidy of
    30  *   the codebase (a few unnoticed bugs removed)
    31  * - Reads makernote data for 6 families of camera (5 makes)
    32  * - Tags now stored in directories... use the IFD_* constants to refer to the
    33  *   image file directory you require (Exif, Interop, GPS and Makernote*) --
    34  *   this avoids collisions where two tags share the same code
    35  * - Takes componentCount of unknown tags into account
    36  * - Now understands GPS tags (thanks to Colin Briton for his help with this)
    37  * - Some other bug fixes, pointed out by users around the world.  Thanks!
    38  * Modified 27 Nov 2002 (v2.0)
    39  * - Renamed to ExifReader
    40  * - Moved to new package com.drew.metadata.exif
    41  * Modified since, however changes have not been logged.  See release notes for
    42  * library-wide modifications.
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    4320 */
    4421package com.drew.metadata.exif;
    4522
    46 import com.drew.imaging.jpeg.JpegProcessingException;
    47 import com.drew.imaging.jpeg.JpegSegmentData;
    48 import com.drew.imaging.jpeg.JpegSegmentReader;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
    4925import com.drew.lang.Rational;
     26import com.drew.lang.annotations.NotNull;
    5027import com.drew.metadata.Directory;
    5128import com.drew.metadata.Metadata;
    5229import com.drew.metadata.MetadataReader;
    5330
    54 import java.io.File;
    55 import java.io.InputStream;
    56 import java.util.HashMap;
     31import java.util.HashSet;
     32import java.util.Set;
    5733
    5834/**
    59  * Extracts Exif data from a JPEG header segment, providing information about the
    60  * camera/scanner/capture device (if available).  Information is encapsulated in
    61  * an <code>Metadata</code> object.
    62  * @author  Drew Noakes http://drewnoakes.com
     35 * 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.
     37 *
     38 * @author Drew Noakes http://drewnoakes.com
    6339 */
    6440public class ExifReader implements MetadataReader
    6541{
    66     /**
    67      * The JPEG segment as an array of bytes.
    68      */
    69     private final byte[] _data;
    70 
    71     /**
    72      * Represents the native byte ordering used in the JPEG segment.  If true,
    73      * then we're using Motorolla ordering (Big endian), else we're using Intel
    74      * ordering (Little endian).
    75      */
    76     private boolean _isMotorollaByteOrder;
    77 
    78     /**
    79      * Bean instance to store information about the image and camera/scanner/capture
    80      * device.
    81      */
    82     private Metadata _metadata;
    83 
    84     /**
    85      * The number of bytes used per format descriptor.
    86      */
    87     private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
    88 
    89     /**
    90      * The number of formats known.
    91      */
     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. */
     45    @NotNull
     46    private static final int[] BYTES_PER_FORMAT = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 };
     47
     48    /** The number of formats known. */
    9249    private static final int MAX_FORMAT_CODE = 12;
    9350
    9451    // Format types
    95     // Note: Cannot use the DataFormat enumeration in the case statement that uses these tags.
    96     //       Is there a better way?
     52    // TODO use an enum for these?
     53    /** An 8-bit unsigned integer. */
    9754    private static final int FMT_BYTE = 1;
     55    /** A fixed-length character string. */
    9856    private static final int FMT_STRING = 2;
     57    /** An unsigned 16-bit integer. */
    9958    private static final int FMT_USHORT = 3;
     59    /** An unsigned 32-bit integer. */
    10060    private static final int FMT_ULONG = 4;
    10161    private static final int FMT_URATIONAL = 5;
     62    /** An 8-bit signed integer. */
    10263    private static final int FMT_SBYTE = 6;
    10364    private static final int FMT_UNDEFINED = 7;
     65    /** A signed 16-bit integer. */
    10466    private static final int FMT_SSHORT = 8;
     67    /** A signed 32-bit integer. */
    10568    private static final int FMT_SLONG = 9;
    10669    private static final int FMT_SRATIONAL = 10;
     70    /** A 32-bit floating point number. */
    10771    private static final int FMT_SINGLE = 11;
     72    /** A 64-bit floating point number. */
    10873    private static final int FMT_DOUBLE = 12;
    10974
    110     public static final int TAG_EXIF_OFFSET = 0x8769;
     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. */
    11178    public static final int TAG_INTEROP_OFFSET = 0xA005;
     79    /** This tag is a pointer to the Exif GPS IFD. */
    11280    public static final int TAG_GPS_INFO_OFFSET = 0x8825;
    113     public static final int TAG_MAKER_NOTE = 0x927C;
     81    /** This tag is a pointer to the Exif Makernote IFD. */
     82    public static final int TAG_MAKER_NOTE_OFFSET = 0x927C;
    11483
    11584    public static final int TIFF_HEADER_START_OFFSET = 6;
    116 
    117     /**
    118      * Creates an ExifReader for a JpegSegmentData object.
    119      * @param segmentData
    120      */
    121     public ExifReader(JpegSegmentData segmentData)
    122     {
    123         this(segmentData.getSegment(JpegSegmentReader.SEGMENT_APP1));
    124     }
    125 
    126     /**
    127      * Creates an ExifReader for a Jpeg file.
    128      * @param file
    129      * @throws JpegProcessingException
    130      */
    131     public ExifReader(File file) throws JpegProcessingException
    132     {
    133         this(new JpegSegmentReader(file).readSegment(JpegSegmentReader.SEGMENT_APP1));
    134     }
    135 
    136     /**
    137      * Creates an ExifReader for a Jpeg stream.
    138      * @param is JPEG stream. Stream will be closed.
    139      */
    140     public ExifReader(InputStream is) throws JpegProcessingException
    141     {
    142         this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APP1));
    143     }
    144 
    145     /**
    146      * Creates an ExifReader for the given JPEG header segment.
    147      */
    148     public ExifReader(byte[] data)
    149     {
    150         _data = data;
    151     }
    152 
    153     /**
    154      * Performs the Exif data extraction, returning a new instance of <code>Metadata</code>.
    155      */
    156     public Metadata extract()
    157     {
    158         return extract(new Metadata());
    159     }
    16085
    16186    /**
    16287     * Performs the Exif data extraction, adding found values to the specified
    16388     * 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.
    16492     */
    165     public Metadata extract(Metadata metadata)
    166     {
    167         _metadata = metadata;
    168         if (_data==null)
    169             return _metadata;
    170 
    171         // once we know there's some data, create the directory and start working on it
    172         ExifDirectory directory = (ExifDirectory)_metadata.getDirectory(ExifDirectory.class);
     93    public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
     94    {
     95        final ExifSubIFDDirectory directory = metadata.getOrCreateDirectory(ExifSubIFDDirectory.class);
    17396
    17497        // check for the header length
    175         if (_data.length<=14) {
     98        if (reader.getLength() <= 14) {
    17699            directory.addError("Exif data segment must contain at least 14 bytes");
    177             return _metadata;
     100            return;
    178101        }
    179102
    180103        // check for the header preamble
    181         if (!"Exif\0\0".equals(new String(_data, 0, 6))) {
    182             directory.addError("Exif data segment doesn't begin with 'Exif'");
    183             return _metadata;
    184         }
    185 
     104        try {
     105            if (!reader.getString(0, 6).equals("Exif\0\0")) {
     106                directory.addError("Exif data segment doesn't begin with 'Exif'");
     107                return;
     108            }
     109
     110            extractIFD(metadata, metadata.getOrCreateDirectory(ExifIFD0Directory.class), TIFF_HEADER_START_OFFSET, reader);
     111        } catch (BufferBoundsException e) {
     112            directory.addError("Exif data segment ended prematurely");
     113        }
     114    }
     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    {
    186136        // this should be either "MM" or "II"
    187         String byteOrderIdentifier = new String(_data, 6, 2);
    188         if (!setByteOrder(byteOrderIdentifier)) {
     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 {
    189144            directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
    190             return _metadata;
     145            return;
    191146        }
    192147
    193148        // Check the next two values for correctness.
    194         if (get16Bits(8)!=0x2a) {
    195             directory.addError("Invalid Exif start - should have 0x2A at offset 8 in Exif header");
    196             return _metadata;
    197         }
    198 
    199         int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET;
    200 
    201         // David Ekholm sent an digital camera image that has this problem
    202         if (firstDirectoryOffset>=_data.length - 1) {
     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) {
    203164            directory.addError("First exif directory offset is beyond end of Exif data segment");
    204165            // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
     
    206167        }
    207168
    208         HashMap processedDirectoryOffsets = new HashMap();
    209 
    210         // 0th IFD (we merge with Exif IFD)
    211         processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET);
     169        Set<Integer> processedDirectoryOffsets = new HashSet<Integer>();
     170
     171        processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, tiffHeaderOffset, metadata, reader);
    212172
    213173        // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
    214         storeThumbnailBytes(directory, TIFF_HEADER_START_OFFSET);
    215 
    216         return _metadata;
    217     }
    218 
    219     private void storeThumbnailBytes(ExifDirectory exifDirectory, int tiffHeaderOffset)
    220     {
    221         if (!exifDirectory.containsTag(ExifDirectory.TAG_COMPRESSION))
    222                 return;
    223 
    224         if (!exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH) ||
    225             !exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET))
    226             return;
    227 
    228         try {
    229             int offset = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_OFFSET);
    230             int length = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_LENGTH);
    231             byte[] result = new byte[length];
    232             for (int i = 0; i<result.length; i++) {
    233                 result[i] = _data[tiffHeaderOffset + offset + i];
    234             }
    235             exifDirectory.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);
    236         } catch (Throwable e) {
    237             exifDirectory.addError("Unable to extract thumbnail: " + e.getMessage());
    238         }
    239     }
    240 
    241     private boolean setByteOrder(String byteOrderIdentifier)
    242     {
    243         if ("MM".equals(byteOrderIdentifier)) {
    244             _isMotorollaByteOrder = true;
    245         } else if ("II".equals(byteOrderIdentifier)) {
    246             _isMotorollaByteOrder = false;
    247         } else {
    248             return false;
    249         }
    250         return true;
     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        }
    251187    }
    252188
    253189    /**
    254190     * Process one of the nested Tiff IFD directories.
     191     * <p/>
     192     * Header
    255193     * 2 bytes: number of tags
    256      * for each tag
    257      *   2 bytes: tag type
    258      *   2 bytes: format code
    259      *   4 bytes: component count
     194     * <p/>
     195     * Then for each tag
     196     * 2 bytes: tag type
     197     * 2 bytes: format code
     198     * 4 bytes: component count
    260199     */
    261     private void processDirectory(Directory directory, HashMap processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset)
     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
    262201    {
    263202        // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
    264         if (processedDirectoryOffsets.containsKey(new Integer(dirStartOffset)))
     203        if (processedDirectoryOffsets.contains(Integer.valueOf(dirStartOffset)))
    265204            return;
    266205
    267206        // remember that we've visited this directory so that we don't visit it again later
    268         processedDirectoryOffsets.put(new Integer(dirStartOffset), "processed");
    269 
    270         if (dirStartOffset>=_data.length || dirStartOffset<0) {
    271             directory.addError("Ignored directory marked to start outside data segement");
    272             return;
    273         }
    274 
    275         if (!isDirectoryLengthValid(dirStartOffset, tiffHeaderOffset)) {
     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()) {
    276219            directory.addError("Illegally sized directory");
    277220            return;
    278221        }
    279222
    280         // First two bytes in the IFD are the number of tags in this directory
    281         int dirTagCount = get16Bits(dirStartOffset);
    282 
    283223        // Handle each tag in this directory
    284         for (int tagNumber = 0; tagNumber<dirTagCount; tagNumber++)
    285         {
     224        for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) {
    286225            final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber);
    287226
    288227            // 2 bytes for the tag type
    289             final int tagType = get16Bits(tagOffset);
     228            final int tagType = reader.getUInt16(tagOffset);
    290229
    291230            // 2 bytes for the format code
    292             final int formatCode = get16Bits(tagOffset + 2);
    293             if (formatCode<1 || formatCode>MAX_FORMAT_CODE) {
    294                 directory.addError("Invalid format code: " + formatCode);
    295                 continue;
     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                return;
    296237            }
    297238
    298239            // 4 bytes dictate the number of components in this tag's data
    299             final int componentCount = get32Bits(tagOffset + 4);
    300             if (componentCount<0) {
    301                 directory.addError("Negative component count in EXIF");
     240            final int componentCount = reader.getInt32(tagOffset + 4);
     241            if (componentCount < 0) {
     242                directory.addError("Negative TIFF tag component count");
    302243                continue;
    303244            }
    304245            // each component may have more than one byte... calculate the total number of bytes
    305246            final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode];
    306             final int tagValueOffset = calculateTagValueOffset(byteCount, tagOffset, tiffHeaderOffset);
    307             if (tagValueOffset<0 || tagValueOffset > _data.length) {
    308                 directory.addError("Illegal pointer offset value in EXIF");
     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");
    309266                continue;
    310267            }
     
    312269            // Check that this tag isn't going to allocate outside the bounds of the data array.
    313270            // This addresses an uncommon OutOfMemoryError.
    314             if (byteCount < 0 || tagValueOffset + byteCount > _data.length)
    315             {
     271            if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) {
    316272                directory.addError("Illegal number of bytes: " + byteCount);
    317273                continue;
    318274            }
    319275
    320             // Calculate the value as an offset for cases where the tag represents directory
    321             final int subdirOffset = tiffHeaderOffset + get32Bits(tagValueOffset);
    322 
    323276            switch (tagType) {
    324                 case TAG_EXIF_OFFSET:
    325                     processDirectory(_metadata.getDirectory(ExifDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
     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);
    326280                    continue;
    327                 case TAG_INTEROP_OFFSET:
    328                     processDirectory(_metadata.getDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
     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);
    329285                    continue;
    330                 case TAG_GPS_INFO_OFFSET:
    331                     processDirectory(_metadata.getDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
     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);
    332290                    continue;
    333                 case TAG_MAKER_NOTE:
    334                     processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset);
     291                }
     292                case TAG_MAKER_NOTE_OFFSET: {
     293                    processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset, metadata, reader);
    335294                    continue;
    336                 default:
    337                     processTag(directory, tagType, tagValueOffset, componentCount, formatCode);
     295                }
     296                default: {
     297                    processTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader);
    338298                    break;
     299                }
    339300            }
    340301        }
     
    342303        // at the end of each IFD is an optional link to the next IFD
    343304        final int finalTagOffset = calculateTagOffset(dirStartOffset, dirTagCount);
    344         int nextDirectoryOffset = get32Bits(finalTagOffset);
    345         if (nextDirectoryOffset!=0) {
     305        int nextDirectoryOffset = reader.getInt32(finalTagOffset);
     306        if (nextDirectoryOffset != 0) {
    346307            nextDirectoryOffset += tiffHeaderOffset;
    347             if (nextDirectoryOffset>=_data.length) {
     308            if (nextDirectoryOffset >= reader.getLength()) {
    348309                // Last 4 bytes of IFD reference another IFD with an address that is out of bounds
    349310                // Note this could have been caused by jhead 1.3 cropping too much
     
    353314                return;
    354315            }
    355             // the next directory is of same type as this one
    356             processDirectory(directory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset);
    357         }
    358     }
    359 
    360     private void processMakerNote(int subdirOffset, HashMap processedDirectoryOffsets, int tiffHeaderOffset)
     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
    361323    {
    362324        // Determine the camera model and makernote format
    363         Directory exifDirectory = _metadata.getDirectory(ExifDirectory.class);
    364 
    365         if (exifDirectory==null)
    366             return;
    367 
    368         String cameraModel = exifDirectory.getString(ExifDirectory.TAG_MAKE);
    369         final String firstTwoChars = new String(_data, subdirOffset, 2);
    370         final String firstThreeChars = new String(_data, subdirOffset, 3);
    371         final String firstFourChars = new String(_data, subdirOffset, 4);
    372         final String firstFiveChars = new String(_data, subdirOffset, 5);
    373         final String firstSixChars = new String(_data, subdirOffset, 6);
    374         final String firstSevenChars = new String(_data, subdirOffset, 7);
    375         final String firstEightChars = new String(_data, subdirOffset, 8);
    376         if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars))
    377         {
     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)) {
    378342            // Olympus Makernote
    379             // Epson and Agfa use Olypus maker note standard, see:
    380             //     http://www.ozhiker.com/electronics/pjmt/jpeg_info/
    381             processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset);
    382         }
    383         else if (cameraModel!=null && cameraModel.trim().toUpperCase().startsWith("NIKON"))
    384         {
    385             if ("Nikon".equals(firstFiveChars))
    386             {
     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)) {
    387347                /* There are two scenarios here:
    388348                 * Type 1:                  **
     
    393353                 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
    394354                 */
    395                 if (_data[subdirOffset+6]==1)
    396                     processDirectory(_metadata.getDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset);
    397                 else if (_data[subdirOffset+6]==2)
    398                     processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10);
    399                 else
    400                     exifDirectory.addError("Unsupported makernote data ignored.");
    401             }
     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);
    402388            else
    403             {
    404                 // The IFD begins with the first MakerNote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
    405                 processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
    406             }
    407         }
    408         else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars))
    409         {
    410             processDirectory(_metadata.getDirectory(SonyMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset);
    411         }
    412         else if ("KDK".equals(firstThreeChars))
    413         {
    414             processDirectory(_metadata.getDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset);
    415         }
    416         else if ("Canon".equalsIgnoreCase(cameraModel))
    417         {
    418             processDirectory(_metadata.getDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
    419         }
    420         else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("CASIO"))
    421         {
    422             if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
    423                 processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset);
    424             else
    425                 processDirectory(_metadata.getDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
    426         }
    427         else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel))
    428         {
    429             // TODO make this field a passed parameter, to avoid threading issues
    430             boolean byteOrderBefore = _isMotorollaByteOrder;
     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();
    431392            // bug in fujifilm makernote ifd means we temporarily use Intel byte ordering
    432             _isMotorollaByteOrder = false;
     393            reader.setMotorolaByteOrder(false);
    433394            // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
    434395            // IFD, though the offset is relative to the start of the makernote, not the TIFF
    435396            // header (like everywhere else)
    436             int ifdStart = subdirOffset + get32Bits(subdirOffset + 8);
    437             processDirectory(_metadata.getDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset);
    438             _isMotorollaByteOrder = byteOrderBefore;
    439         }
    440         else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("MINOLTA"))
    441         {
     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")) {
    442401            // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
    443402            // area that commences immediately.
    444             processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
    445         }
    446         else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars))
    447         {
    448             // This Konica data is not understood.  Header identified in accordance with information at this site:
    449             // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html
    450             // TODO determine how to process the information described at the above website
    451             exifDirectory.addError("Unsupported Konica/Minolta data ignored.");
    452         }
    453         else if ("KYOCERA".equals(firstSevenChars))
    454         {
     403            processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
     404        } else if ("KYOCERA".equals(firstSevenChars)) {
    455405            // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
    456             processDirectory(_metadata.getDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset);
    457         }
    458         else if ("Panasonic\u0000\u0000\u0000".equals(new String(_data, subdirOffset, 12)))
    459         {
     406            processDirectory(metadata.getOrCreateDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset, metadata, reader);
     407        } else if ("Panasonic\u0000\u0000\u0000".equals(reader.getString(subdirOffset, 12))) {
    460408            // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
    461409            // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
    462410            // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
    463             processDirectory(_metadata.getDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset);
    464         }
    465         else if ("AOC\u0000".equals(firstFourChars))
    466         {
     411            processDirectory(metadata.getOrCreateDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader);
     412        } else if ("AOC\u0000".equals(firstFourChars)) {
    467413            // NON-Standard TIFF IFD Data using Casio Type 2 Tags
    468414            // IFD has no Next-IFD pointer at end of IFD, and
     
    470416            // Observed for:
    471417            // - Pentax ist D
    472             processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, subdirOffset);
    473         }
    474         else if (cameraModel!=null && (cameraModel.toUpperCase().startsWith("PENTAX") || cameraModel.toUpperCase().startsWith("ASAHI")))
    475         {
     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"))) {
    476420            // NON-Standard TIFF IFD Data using Pentax Tags
    477421            // IFD has no Next-IFD pointer at end of IFD, and
     
    480424            // - PENTAX Optio 330
    481425            // - PENTAX Optio 430
    482             processDirectory(_metadata.getDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset);
    483         }
    484         else
    485         {
     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 {
    486433            // TODO how to store makernote data when it's not from a supported camera model?
    487434            // this is difficult as the starting offset is not known.  we could look for it...
    488             exifDirectory.addError("Unsupported makernote data ignored.");
    489         }
    490     }
    491 
    492     private boolean isDirectoryLengthValid(int dirStartOffset, int tiffHeaderOffset)
    493     {
    494         int dirTagCount = get16Bits(dirStartOffset);
    495         int dirLength = (2 + (12 * dirTagCount) + 4);
    496         if (dirLength + dirStartOffset + tiffHeaderOffset>=_data.length) {
    497             // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier might trigger this
    498             return false;
    499         }
    500         return true;
    501     }
    502 
    503     private void processTag(Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode)
     435        }
     436    }
     437
     438    private void processTag(@NotNull Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, @NotNull final BufferReader reader) throws BufferBoundsException
    504439    {
    505440        // Directory simply stores raw values
    506441        // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions
    507         switch (formatCode)
    508         {
     442        switch (formatCode) {
    509443            case FMT_UNDEFINED:
    510444                // this includes exif user comments
    511                 final byte[] tagBytes = new byte[componentCount];
    512                 final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode];
    513                 for (int i=0; i<byteCount; i++)
    514                     tagBytes[i] = _data[tagValueOffset + i];
    515                 directory.setByteArray(tagType, tagBytes);
     445                directory.setByteArray(tagType, reader.getBytes(tagValueOffset, componentCount));
    516446                break;
    517447            case FMT_STRING:
    518                 directory.setString(tagType, readString(tagValueOffset, componentCount));
     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*/
    519474                break;
    520475            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;
    521485            case FMT_URATIONAL:
    522                 if (componentCount==1) {
    523                     Rational rational = new Rational(get32Bits(tagValueOffset), get32Bits(tagValueOffset + 4));
    524                     directory.setRational(tagType, rational);
    525                 } else {
     486                if (componentCount == 1) {
     487                    directory.setRational(tagType, new Rational(reader.getUInt32(tagValueOffset), reader.getUInt32(tagValueOffset + 4)));
     488                } else if (componentCount > 1) {
    526489                    Rational[] rationals = new Rational[componentCount];
    527                     for (int i = 0; i<componentCount; i++)
    528                         rationals[i] = new Rational(get32Bits(tagValueOffset + (8 * i)), get32Bits(tagValueOffset + 4 + (8 * i)));
     490                    for (int i = 0; i < componentCount; i++)
     491                        rationals[i] = new Rational(reader.getUInt32(tagValueOffset + (8 * i)), reader.getUInt32(tagValueOffset + 4 + (8 * i)));
    529492                    directory.setRationalArray(tagType, rationals);
    530493                }
    531494                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
    532520            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;
    533530            case FMT_BYTE:
    534                 if (componentCount==1) {
    535                     // this may need to be a byte, but I think casting to int is fine
    536                     int b = _data[tagValueOffset];
    537                     directory.setInt(tagType, b);
     531                if (componentCount == 1) {
     532                    directory.setInt(tagType, reader.getUInt8(tagValueOffset));
    538533                } else {
    539534                    int[] bytes = new int[componentCount];
    540                     for (int i = 0; i<componentCount; i++)
    541                         bytes[i] = _data[tagValueOffset + i];
     535                    for (int i = 0; i < componentCount; i++)
     536                        bytes[i] = reader.getUInt8(tagValueOffset + i);
    542537                    directory.setIntArray(tagType, bytes);
    543538                }
    544539                break;
    545             case FMT_SINGLE:
    546             case FMT_DOUBLE:
    547                 if (componentCount==1) {
    548                     int i = _data[tagValueOffset];
     540            case FMT_USHORT:
     541                if (componentCount == 1) {
     542                    int i = reader.getUInt16(tagValueOffset);
    549543                    directory.setInt(tagType, i);
    550544                } else {
    551545                    int[] ints = new int[componentCount];
    552                     for (int i = 0; i<componentCount; i++)
    553                         ints[i] = _data[tagValueOffset + i];
     546                    for (int i = 0; i < componentCount; i++)
     547                        ints[i] = reader.getUInt16(tagValueOffset + (i * 2));
    554548                    directory.setIntArray(tagType, ints);
    555549                }
    556550                break;
    557             case FMT_USHORT:
    558551            case FMT_SSHORT:
    559                 if (componentCount==1) {
    560                     int i = get16Bits(tagValueOffset);
     552                if (componentCount == 1) {
     553                    int i = reader.getInt16(tagValueOffset);
    561554                    directory.setInt(tagType, i);
    562555                } else {
    563556                    int[] ints = new int[componentCount];
    564                     for (int i = 0; i<componentCount; i++)
    565                         ints[i] = get16Bits(tagValueOffset + (i * 2));
     557                    for (int i = 0; i < componentCount; i++)
     558                        ints[i] = reader.getInt16(tagValueOffset + (i * 2));
    566559                    directory.setIntArray(tagType, ints);
    567560                }
     
    569562            case FMT_SLONG:
    570563            case FMT_ULONG:
    571                 if (componentCount==1) {
    572                     int i = get32Bits(tagValueOffset);
     564                // NOTE 'long' in this case means 32 bit, not 64
     565                if (componentCount == 1) {
     566                    int i = reader.getInt32(tagValueOffset);
    573567                    directory.setInt(tagType, i);
    574568                } else {
    575569                    int[] ints = new int[componentCount];
    576                     for (int i = 0; i<componentCount; i++)
    577                         ints[i] = get32Bits(tagValueOffset + (i * 4));
     570                    for (int i = 0; i < componentCount; i++)
     571                        ints[i] = reader.getInt32(tagValueOffset + (i * 4));
    578572                    directory.setIntArray(tagType, ints);
    579573                }
     
    584578    }
    585579
    586     private int calculateTagValueOffset(int byteCount, int dirEntryOffset, int tiffHeaderOffset)
    587     {
    588         if (byteCount>4) {
    589             // If its bigger than 4 bytes, the dir entry contains an offset.
    590             // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an
    591             // offset relative to the start of the makernote itself, not the TIFF segment.
    592             final int offsetVal = get32Bits(dirEntryOffset + 8);
    593             if (offsetVal + byteCount>_data.length) {
    594                 // Bogus pointer offset and / or bytecount value
    595                 return -1; // signal error
    596             }
    597             return tiffHeaderOffset + offsetVal;
    598         } else {
    599             // 4 bytes or less and value is in the dir entry itself
    600             return dirEntryOffset + 8;
    601         }
    602     }
    603 
    604     /**
    605      * Creates a String from the _data buffer starting at the specified offset,
    606      * and ending where byte=='\0' or where length==maxLength.
    607      */
    608     private String readString(int offset, int maxLength)
    609     {
    610         int length = 0;
    611         while ((offset + length)<_data.length && _data[offset + length]!='\0' && length<maxLength)
    612             length++;
    613 
    614         return new String(_data, offset, length);
    615     }
    616 
    617580    /**
    618581     * Determine the offset at which a given InteropArray entry begins within the specified IFD.
     582     *
    619583     * @param dirStartOffset the offset at which the IFD starts
    620      * @param entryNumber the zero-based entry number
     584     * @param entryNumber    the zero-based entry number
    621585     */
    622586    private int calculateTagOffset(int dirStartOffset, int entryNumber)
     
    626590        return dirStartOffset + 2 + (12 * entryNumber);
    627591    }
    628 
    629     /**
    630      * Get a 16 bit value from file's native byte order.  Between 0x0000 and 0xFFFF.
    631      */
    632     private int get16Bits(int offset)
    633     {
    634         if (offset<0 || offset+2>_data.length)
    635             throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");
    636 
    637         if (_isMotorollaByteOrder) {
    638             // Motorola - MSB first
    639             return (_data[offset] << 8 & 0xFF00) | (_data[offset + 1] & 0xFF);
    640         } else {
    641             // Intel ordering - LSB first
    642             return (_data[offset + 1] << 8 & 0xFF00) | (_data[offset] & 0xFF);
    643         }
    644     }
    645 
    646     /**
    647      * Get a 32 bit value from file's native byte order.
    648      */
    649     private int get32Bits(int offset)
    650     {
    651         if (offset<0 || offset+4>_data.length)
    652             throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");
    653 
    654         if (_isMotorollaByteOrder) {
    655             // Motorola - MSB first
    656             return (_data[offset] << 24 & 0xFF000000) |
    657                     (_data[offset + 1] << 16 & 0xFF0000) |
    658                     (_data[offset + 2] << 8 & 0xFF00) |
    659                     (_data[offset + 3] & 0xFF);
    660         } else {
    661             // Intel ordering - LSB first
    662             return (_data[offset + 3] << 24 & 0xFF000000) |
    663                     (_data[offset + 2] << 16 & 0xFF0000) |
    664                     (_data[offset + 1] << 8 & 0xFF00) |
    665                     (_data[offset] & 0xFF);
    666         }
    667     }
    668592}
  • trunk/src/com/drew/metadata/exif/FujifilmMakernoteDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    1923import com.drew.lang.Rational;
    20 import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
    2226import com.drew.metadata.TagDescriptor;
    2327
    2428/**
     29 * Provides human-readable string representations of tag values stored in a <code>FujifilmMakernoteDirectory</code>.
     30 * <p/>
    2531 * Fujifilm's digicam added the MakerNote tag from the Year2000's model (e.g.Finepix1400,
    2632 * Finepix4700). It uses IFD format and start from ASCII character 'FUJIFILM', and next 4
    2733 * bytes(value 0x000c) points the offset to first IFD entry. Example of actual data
    2834 * structure is shown below.
    29  *
     35 * <p/>
     36 * <pre><code>
    3037 * :0000: 46 55 4A 49 46 49 4C 4D-0C 00 00 00 0F 00 00 00 :0000: FUJIFILM........
    3138 * :0010: 07 00 04 00 00 00 30 31-33 30 00 10 02 00 08 00 :0010: ......0130......
    32  *
     39 * </code></pre>
     40 * <p/>
    3341 * There are two big differences to the other manufacturers.
    3442 * - Fujifilm's Exif data uses Motorola align, but MakerNote ignores it and uses Intel
     
    3745 *   of TIFF header (same as the other IFD), but Fujifilm counts it from the first byte
    3846 *   of MakerNote itself.
     47 *
     48 * @author Drew Noakes http://drewnoakes.com
    3949 */
    40 public class FujifilmMakernoteDescriptor extends TagDescriptor
     50public class FujifilmMakernoteDescriptor extends TagDescriptor<FujifilmMakernoteDirectory>
    4151{
    42     public FujifilmMakernoteDescriptor(Directory directory)
     52    public FujifilmMakernoteDescriptor(@NotNull FujifilmMakernoteDirectory directory)
    4353    {
    4454        super(directory);
    4555    }
    4656
    47     public String getDescription(int tagType) throws MetadataException
     57    @Nullable
     58    public String getDescription(int tagType)
    4859    {
    4960        switch (tagType) {
     
    5263            case FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE:
    5364                return getWhiteBalanceDescription();
    54             case FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR:
     65            case FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION:
    5566                return getColorDescription();
    5667            case FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE:
     
    6475            case FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE:
    6576                return getFocusModeDescription();
    66             case FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO:
     77            case FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH:
    6778                return getSlowSyncDescription();
    6879            case FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE:
     
    7788                return getAutoExposureWarningDescription();
    7889            default:
    79                 return _directory.getString(tagType);
    80         }
    81     }
    82 
    83     public String getAutoExposureWarningDescription() throws MetadataException
    84     {
    85         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING)) return null;
    86         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING);
    87         switch (value) {
    88             case 0:
    89                 return "AE good";
    90             case 1:
    91                 return "Over exposed (>1/1000s @ F11)";
    92             default:
    93                 return "Unknown (" + value + ")";
    94         }
    95     }
    96 
    97     public String getFocusWarningDescription() throws MetadataException
    98     {
    99         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING)) return null;
    100         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING);
    101         switch (value) {
    102             case 0:
    103                 return "Auto focus good";
    104             case 1:
    105                 return "Out of focus";
    106             default:
    107                 return "Unknown (" + value + ")";
    108         }
    109     }
    110 
    111     public String getBlurWarningDescription() throws MetadataException
    112     {
    113         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING)) return null;
    114         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING);
    115         switch (value) {
    116             case 0:
    117                 return "No blur warning";
    118             case 1:
    119                 return "Blur warning";
    120             default:
    121                 return "Unknown (" + value + ")";
    122         }
    123     }
    124 
    125     public String getContinuousTakingOrAutoBrackettingDescription() throws MetadataException
    126     {
    127         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING)) return null;
    128         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING);
    129         switch (value) {
    130             case 0:
     90                return super.getDescription(tagType);
     91        }
     92    }
     93
     94    @Nullable
     95    public String getSharpnessDescription()
     96    {
     97        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS);
     98        if (value==null)
     99            return null;
     100        switch (value) {
     101            case 1:
     102                return "Softest";
     103            case 2:
     104                return "Soft";
     105            case 3:
     106                return "Normal";
     107            case 4:
     108                return "Hard";
     109            case 5:
     110                return "Hardest";
     111            default:
     112                return "Unknown (" + value + ")";
     113        }
     114    }
     115
     116    @Nullable
     117    public String getWhiteBalanceDescription()
     118    {
     119        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE);
     120        if (value==null)
     121            return null;
     122        switch (value) {
     123            case 0:
     124                return "Auto";
     125            case 256:
     126                return "Daylight";
     127            case 512:
     128                return "Cloudy";
     129            case 768:
     130                return "DaylightColor-fluorescence";
     131            case 769:
     132                return "DaywhiteColor-fluorescence";
     133            case 770:
     134                return "White-fluorescence";
     135            case 1024:
     136                return "Incandescence";
     137            case 3840:
     138                return "Custom white balance";
     139            default:
     140                return "Unknown (" + value + ")";
     141        }
     142    }
     143
     144    @Nullable
     145    public String getColorDescription()
     146    {
     147        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION);
     148        if (value==null)
     149            return null;
     150        switch (value) {
     151            case 0:
     152                return "Normal (STD)";
     153            case 256:
     154                return "High (HARD)";
     155            case 512:
     156                return "Low (ORG)";
     157            default:
     158                return "Unknown (" + value + ")";
     159        }
     160    }
     161
     162    @Nullable
     163    public String getToneDescription()
     164    {
     165        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE);
     166        if (value==null)
     167            return null;
     168        switch (value) {
     169            case 0:
     170                return "Normal (STD)";
     171            case 256:
     172                return "High (HARD)";
     173            case 512:
     174                return "Low (ORG)";
     175            default:
     176                return "Unknown (" + value + ")";
     177        }
     178    }
     179
     180    @Nullable
     181    public String getFlashModeDescription()
     182    {
     183        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE);
     184        if (value==null)
     185            return null;
     186        switch (value) {
     187            case 0:
     188                return "Auto";
     189            case 1:
     190                return "On";
     191            case 2:
    131192                return "Off";
    132             case 1:
    133                 return "On";
    134             default:
    135                 return "Unknown (" + value + ")";
    136         }
    137     }
    138 
    139     public String getPictureModeDescription() throws MetadataException
    140     {
    141         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE)) return null;
    142         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE);
     193            case 3:
     194                return "Red-eye reduction";
     195            default:
     196                return "Unknown (" + value + ")";
     197        }
     198    }
     199
     200    @Nullable
     201    public String getFlashStrengthDescription()
     202    {
     203        Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH);
     204        if (value==null)
     205            return null;
     206        return value.toSimpleString(false) + " EV (Apex)";
     207    }
     208
     209    @Nullable
     210    public String getMacroDescription()
     211    {
     212        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO);
     213    }
     214
     215    @Nullable
     216    public String getFocusModeDescription()
     217    {
     218        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE);
     219        if (value==null)
     220            return null;
     221        switch (value) {
     222            case 0:
     223                return "Auto focus";
     224            case 1:
     225                return "Manual focus";
     226            default:
     227                return "Unknown (" + value + ")";
     228        }
     229    }
     230
     231    @Nullable
     232    public String getSlowSyncDescription()
     233    {
     234        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH);
     235    }
     236
     237    @Nullable
     238    public String getPictureModeDescription()
     239    {
     240        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE);
     241        if (value==null)
     242            return null;
    143243        switch (value) {
    144244            case 0:
     
    165265    }
    166266
    167     public String getSlowSyncDescription() throws MetadataException
    168     {
    169         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO)) return null;
    170         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO);
     267    @Nullable
     268    public String getContinuousTakingOrAutoBrackettingDescription()
     269    {
     270        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING);
     271    }
     272
     273    @Nullable
     274    public String getBlurWarningDescription()
     275    {
     276        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING);
     277        if (value==null)
     278            return null;
     279        switch (value) {
     280            case 0:
     281                return "No blur warning";
     282            case 1:
     283                return "Blur warning";
     284            default:
     285                return "Unknown (" + value + ")";
     286        }
     287    }
     288
     289    @Nullable
     290    public String getFocusWarningDescription()
     291    {
     292        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING);
     293        if (value==null)
     294            return null;
     295        switch (value) {
     296            case 0:
     297                return "Auto focus good";
     298            case 1:
     299                return "Out of focus";
     300            default:
     301                return "Unknown (" + value + ")";
     302        }
     303    }
     304
     305    @Nullable
     306    public String getAutoExposureWarningDescription()
     307    {
     308        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING);
     309        if (value==null)
     310            return null;
     311        switch (value) {
     312            case 0:
     313                return "AE good";
     314            case 1:
     315                return "Over exposed (>1/1000s @ F11)";
     316            default:
     317                return "Unknown (" + value + ")";
     318        }
     319    }
     320
     321
     322    @Nullable
     323    private String getOnOffDescription(final int tagType)
     324    {
     325        final Integer value = _directory.getInteger(tagType);
     326        if (value==null)
     327            return null;
    171328        switch (value) {
    172329            case 0:
     
    178335        }
    179336    }
    180 
    181     public String getFocusModeDescription() throws MetadataException
    182     {
    183         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE)) return null;
    184         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE);
    185         switch (value) {
    186             case 0:
    187                 return "Auto focus";
    188             case 1:
    189                 return "Manual focus";
    190             default:
    191                 return "Unknown (" + value + ")";
    192         }
    193     }
    194 
    195     public String getMacroDescription() throws MetadataException
    196     {
    197         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO)) return null;
    198         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO);
    199         switch (value) {
    200             case 0:
    201                 return "Off";
    202             case 1:
    203                 return "On";
    204             default:
    205                 return "Unknown (" + value + ")";
    206         }
    207     }
    208 
    209     public String getFlashStrengthDescription() throws MetadataException
    210     {
    211         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH)) return null;
    212         Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH);
    213         return value.toSimpleString(false) + " EV (Apex)";
    214     }
    215 
    216     public String getFlashModeDescription() throws MetadataException
    217     {
    218         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE)) return null;
    219         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE);
    220         switch (value) {
    221             case 0:
    222                 return "Auto";
    223             case 1:
    224                 return "On";
    225             case 2:
    226                 return "Off";
    227             case 3:
    228                 return "Red-eye reduction";
    229             default:
    230                 return "Unknown (" + value + ")";
    231         }
    232     }
    233 
    234     public String getToneDescription() throws MetadataException
    235     {
    236         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE)) return null;
    237         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE);
    238         switch (value) {
    239             case 0:
    240                 return "Normal (STD)";
    241             case 256:
    242                 return "High (HARD)";
    243             case 512:
    244                 return "Low (ORG)";
    245             default:
    246                 return "Unknown (" + value + ")";
    247         }
    248     }
    249 
    250     public String getColorDescription() throws MetadataException
    251     {
    252         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR)) return null;
    253         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR);
    254         switch (value) {
    255             case 0:
    256                 return "Normal (STD)";
    257             case 256:
    258                 return "High";
    259             case 512:
    260                 return "Low (ORG)";
    261             default:
    262                 return "Unknown (" + value + ")";
    263         }
    264     }
    265 
    266     public String getWhiteBalanceDescription() throws MetadataException
    267     {
    268         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE)) return null;
    269         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE);
    270         switch (value) {
    271             case 0:
    272                 return "Auto";
    273             case 256:
    274                 return "Daylight";
    275             case 512:
    276                 return "Cloudy";
    277             case 768:
    278                 return "DaylightColor-fluorescence";
    279             case 769:
    280                 return "DaywhiteColor-fluorescence";
    281             case 770:
    282                 return "White-fluorescence";
    283             case 1024:
    284                 return "Incandenscense";
    285             case 3840:
    286                 return "Custom white balance";
    287             default:
    288                 return "Unknown (" + value + ")";
    289         }
    290     }
    291 
    292     public String getSharpnessDescription() throws MetadataException
    293     {
    294         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS)) return null;
    295         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS);
    296         switch (value) {
    297             case 1:
    298                 return "Softest";
    299             case 2:
    300                 return "Soft";
    301             case 3:
    302                 return "Normal";
    303             case 4:
    304                 return "Hard";
    305             case 5:
    306                 return "Hardest";
    307             default:
    308                 return "Unknown (" + value + ")";
    309         }
    310     }
    311337}
  • trunk/src/com/drew/metadata/exif/FujifilmMakernoteDirectory.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    97 *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 27-Nov-2002 10:10:47 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Fujifilm cameras.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class FujifilmMakernoteDirectory extends Directory
    2734{
    2835    public static final int TAG_FUJIFILM_MAKERNOTE_VERSION = 0x0000;
    29     public static final int TAG_FUJIFILM_QUALITY = 0x1000;
    30     public static final int TAG_FUJIFILM_SHARPNESS = 0x1001;
    31     public static final int TAG_FUJIFILM_WHITE_BALANCE = 0x1002;
    32     public static final int TAG_FUJIFILM_COLOR = 0x1003;
    33     public static final int TAG_FUJIFILM_TONE = 0x1004;
    34     public static final int TAG_FUJIFILM_FLASH_MODE = 0x1010;
    35     public static final int TAG_FUJIFILM_FLASH_STRENGTH = 0x1011;
    36     public static final int TAG_FUJIFILM_MACRO = 0x1020;
    37     public static final int TAG_FUJIFILM_FOCUS_MODE = 0x1021;
    38     public static final int TAG_FUJIFILM_SLOW_SYNCHRO = 0x1030;
    39     public static final int TAG_FUJIFILM_PICTURE_MODE = 0x1031;
    40     public static final int TAG_FUJIFILM_UNKNOWN_1 = 0x1032;
    41     public static final int TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING = 0x1100;
    42     public static final int TAG_FUJIFILM_UNKNOWN_2 = 0x1200;
    43     public static final int TAG_FUJIFILM_BLUR_WARNING = 0x1300;
    44     public static final int TAG_FUJIFILM_FOCUS_WARNING = 0x1301;
    45     public static final int TAG_FUJIFILM_AE_WARNING = 0x1302;
     36    public static final int TAG_FUJIFILM_QUALITY = 0x1000; // 4096
     37    public static final int TAG_FUJIFILM_SHARPNESS = 0x1001; // 4097
     38    public static final int TAG_FUJIFILM_WHITE_BALANCE = 0x1002; // 4098
     39    public static final int TAG_FUJIFILM_COLOR_SATURATION = 0x1003; // 4099
     40    public static final int TAG_FUJIFILM_TONE = 0x1004; // 4100
     41    public static final int TAG_FUJIFILM_FLASH_MODE = 0x1010; // 4112
     42    public static final int TAG_FUJIFILM_FLASH_STRENGTH = 0x1011; // 4113
     43    public static final int TAG_FUJIFILM_MACRO = 0x1020; // 4128
     44    public static final int TAG_FUJIFILM_FOCUS_MODE = 0x1021; // 4129
     45    public static final int TAG_FUJIFILM_SLOW_SYNCH = 0x1030; // 4144
     46    public static final int TAG_FUJIFILM_PICTURE_MODE = 0x1031; // 4145
     47    public static final int TAG_FUJIFILM_UNKNOWN_1 = 0x1032; // 4146
     48    public static final int TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING = 0x1100; // 4352
     49    public static final int TAG_FUJIFILM_UNKNOWN_2 = 0x1200; // 4608
     50    public static final int TAG_FUJIFILM_BLUR_WARNING = 0x1300; // 4864
     51    public static final int TAG_FUJIFILM_FOCUS_WARNING = 0x1301; // 4865
     52    public static final int TAG_FUJIFILM_AE_WARNING = 0x1302; // 4866
    4653
    47     protected static final HashMap tagNameMap = new HashMap();
     54    @NotNull
     55    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    4856
    4957    static
    5058    {
    51         tagNameMap.put(new Integer(TAG_FUJIFILM_AE_WARNING), "AE Warning");
    52         tagNameMap.put(new Integer(TAG_FUJIFILM_BLUR_WARNING), "Blur Warning");
    53         tagNameMap.put(new Integer(TAG_FUJIFILM_COLOR), "Color");
    54         tagNameMap.put(new Integer(TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING), "Continuous Taking Or Auto Bracketting");
    55         tagNameMap.put(new Integer(TAG_FUJIFILM_FLASH_MODE), "Flash Mode");
    56         tagNameMap.put(new Integer(TAG_FUJIFILM_FLASH_STRENGTH), "Flash Strength");
    57         tagNameMap.put(new Integer(TAG_FUJIFILM_FOCUS_MODE), "Focus Mode");
    58         tagNameMap.put(new Integer(TAG_FUJIFILM_FOCUS_WARNING), "Focus Warning");
    59         tagNameMap.put(new Integer(TAG_FUJIFILM_MACRO), "Macro");
    60         tagNameMap.put(new Integer(TAG_FUJIFILM_MAKERNOTE_VERSION), "Makernote Version");
    61         tagNameMap.put(new Integer(TAG_FUJIFILM_PICTURE_MODE), "Picture Mode");
    62         tagNameMap.put(new Integer(TAG_FUJIFILM_QUALITY), "Quality");
    63         tagNameMap.put(new Integer(TAG_FUJIFILM_SHARPNESS), "Sharpness");
    64         tagNameMap.put(new Integer(TAG_FUJIFILM_SLOW_SYNCHRO), "Slow Synchro");
    65         tagNameMap.put(new Integer(TAG_FUJIFILM_TONE), "Tone");
    66         tagNameMap.put(new Integer(TAG_FUJIFILM_UNKNOWN_1), "Makernote Unknown 1");
    67         tagNameMap.put(new Integer(TAG_FUJIFILM_UNKNOWN_2), "Makernote Unknown 2");
    68         tagNameMap.put(new Integer(TAG_FUJIFILM_WHITE_BALANCE), "White Balance");
     59        _tagNameMap.put(TAG_FUJIFILM_MAKERNOTE_VERSION, "Makernote Version");
     60        _tagNameMap.put(TAG_FUJIFILM_QUALITY, "Quality");
     61        _tagNameMap.put(TAG_FUJIFILM_SHARPNESS, "Sharpness");
     62        _tagNameMap.put(TAG_FUJIFILM_WHITE_BALANCE, "White Balance");
     63        _tagNameMap.put(TAG_FUJIFILM_COLOR_SATURATION, "Color Saturation");
     64        _tagNameMap.put(TAG_FUJIFILM_TONE, "Tone (Contrast)");
     65        _tagNameMap.put(TAG_FUJIFILM_FLASH_MODE, "Flash Mode");
     66        _tagNameMap.put(TAG_FUJIFILM_FLASH_STRENGTH, "Flash Strength");
     67        _tagNameMap.put(TAG_FUJIFILM_MACRO, "Macro");
     68        _tagNameMap.put(TAG_FUJIFILM_FOCUS_MODE, "Focus Mode");
     69        _tagNameMap.put(TAG_FUJIFILM_SLOW_SYNCH, "Slow Synch");
     70        _tagNameMap.put(TAG_FUJIFILM_PICTURE_MODE, "Picture Mode");
     71        _tagNameMap.put(TAG_FUJIFILM_UNKNOWN_1, "Makernote Unknown 1");
     72        _tagNameMap.put(TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING, "Continuous Taking Or Auto Bracketting");
     73        _tagNameMap.put(TAG_FUJIFILM_UNKNOWN_2, "Makernote Unknown 2");
     74        _tagNameMap.put(TAG_FUJIFILM_BLUR_WARNING, "Blur Warning");
     75        _tagNameMap.put(TAG_FUJIFILM_FOCUS_WARNING, "Focus Warning");
     76        _tagNameMap.put(TAG_FUJIFILM_AE_WARNING, "AE Warning");
    6977    }
    7078
     
    7482    }
    7583
     84    @NotNull
    7685    public String getName()
    7786    {
     
    7988    }
    8089
    81     protected HashMap getTagNameMap()
     90    @NotNull
     91    protected HashMap<Integer, String> getTagNameMap()
    8292    {
    83         return tagNameMap;
     93        return _tagNameMap;
    8494    }
    8595}
  • trunk/src/com/drew/metadata/exif/GpsDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 12-Nov-2002 22:27:52 using IntelliJ IDEA.
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.GeoLocation;
    1924import com.drew.lang.Rational;
    20 import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
    2227import com.drew.metadata.TagDescriptor;
    2328
     29import java.text.DecimalFormat;
     30
    2431/**
    25  *
     32 * Provides human-readable string representations of tag values stored in a <code>GpsDirectory</code>.
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2635 */
    27 public class GpsDescriptor extends TagDescriptor
     36public class GpsDescriptor extends TagDescriptor<GpsDirectory>
    2837{
    29     public GpsDescriptor(Directory directory)
     38    public GpsDescriptor(@NotNull GpsDirectory directory)
    3039    {
    3140        super(directory);
    3241    }
    3342
    34     public String getDescription(int tagType) throws MetadataException
     43    @Nullable
     44    public String getDescription(int tagType)
    3545    {
    3646        switch (tagType) {
     47            case GpsDirectory.TAG_GPS_VERSION_ID:
     48                return getGpsVersionIdDescription();
    3749            case GpsDirectory.TAG_GPS_ALTITUDE:
    3850                return getGpsAltitudeDescription();
     
    5769            case GpsDirectory.TAG_GPS_TIME_STAMP:
    5870                return getGpsTimeStampDescription();
     71            case GpsDirectory.TAG_GPS_LONGITUDE:
    5972                // three rational numbers -- displayed in HH"MM"SS.ss
    60             case GpsDirectory.TAG_GPS_LONGITUDE:
    6173                return getGpsLongitudeDescription();
    6274            case GpsDirectory.TAG_GPS_LATITUDE:
     75                // three rational numbers -- displayed in HH"MM"SS.ss
    6376                return getGpsLatitudeDescription();
     77            case GpsDirectory.TAG_GPS_DIFFERENTIAL:
     78                return getGpsDifferentialDescription();
    6479            default:
    65                 return _directory.getString(tagType);
    66         }
    67     }
    68 
    69     public String getGpsLatitudeDescription() throws MetadataException
    70     {
    71         if (!_directory.containsTag(GpsDirectory.TAG_GPS_LATITUDE)) return null;
    72         return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LATITUDE);
    73     }
    74 
    75     public String getGpsLongitudeDescription() throws MetadataException
    76     {
    77         if (!_directory.containsTag(GpsDirectory.TAG_GPS_LONGITUDE)) return null;
    78         return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LONGITUDE);
    79     }
    80 
    81     public String getHoursMinutesSecondsDescription(int tagType) throws MetadataException
    82     {
    83         Rational[] components = _directory.getRationalArray(tagType);
    84         // TODO create an HoursMinutesSecods class ??
    85         int deg = components[0].intValue();
    86         float min = components[1].floatValue();
    87         float sec = components[2].floatValue();
    88         // carry fractions of minutes into seconds -- thanks Colin Briton
    89         sec += (min % 1) * 60;
    90         return String.valueOf(deg) + "\"" + String.valueOf((int)min) + "'" + String.valueOf(sec);
    91     }
    92 
    93     public String getGpsTimeStampDescription() throws MetadataException
     80                return super.getDescription(tagType);
     81        }
     82    }
     83
     84    @Nullable
     85    private String getGpsVersionIdDescription()
     86    {
     87        return convertBytesToVersionString(_directory.getIntArray(GpsDirectory.TAG_GPS_VERSION_ID), 1);
     88    }
     89
     90    @Nullable
     91    public String getGpsLatitudeDescription()
     92    {
     93        GeoLocation location = _directory.getGeoLocation();
     94
     95        if (location == null)
     96            return null;
     97
     98        return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude());
     99    }
     100
     101    @Nullable
     102    public String getGpsLongitudeDescription()
     103    {
     104        GeoLocation location = _directory.getGeoLocation();
     105
     106        if (location == null)
     107            return null;
     108
     109        return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude());
     110    }
     111
     112    @Nullable
     113    public String getGpsTimeStampDescription()
    94114    {
    95115        // time in hour, min, sec
    96         if (!_directory.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP)) return null;
    97116        int[] timeComponents = _directory.getIntArray(GpsDirectory.TAG_GPS_TIME_STAMP);
    98         StringBuffer sbuffer = new StringBuffer();
    99         sbuffer.append(timeComponents[0]);
    100         sbuffer.append(":");
    101         sbuffer.append(timeComponents[1]);
    102         sbuffer.append(":");
    103         sbuffer.append(timeComponents[2]);
    104         sbuffer.append(" UTC");
    105         return sbuffer.toString();
    106     }
    107 
     117        if (timeComponents==null)
     118            return null;
     119        StringBuilder description = new StringBuilder();
     120        description.append(timeComponents[0]);
     121        description.append(":");
     122        description.append(timeComponents[1]);
     123        description.append(":");
     124        description.append(timeComponents[2]);
     125        description.append(" UTC");
     126        return description.toString();
     127    }
     128
     129    @Nullable
    108130    public String getGpsDestinationReferenceDescription()
    109131    {
    110         if (!_directory.containsTag(GpsDirectory.TAG_GPS_DEST_DISTANCE_REF)) return null;
    111         String destRef = _directory.getString(GpsDirectory.TAG_GPS_DEST_DISTANCE_REF).trim();
    112         if ("K".equalsIgnoreCase(destRef)) {
     132        final String value = _directory.getString(GpsDirectory.TAG_GPS_DEST_DISTANCE_REF);
     133        if (value==null)
     134            return null;
     135        String distanceRef = value.trim();
     136        if ("K".equalsIgnoreCase(distanceRef)) {
    113137            return "kilometers";
    114         } else if ("M".equalsIgnoreCase(destRef)) {
     138        } else if ("M".equalsIgnoreCase(distanceRef)) {
    115139            return "miles";
    116         } else if ("N".equalsIgnoreCase(destRef)) {
     140        } else if ("N".equalsIgnoreCase(distanceRef)) {
    117141            return "knots";
    118142        } else {
    119             return "Unknown (" + destRef + ")";
    120         }
    121     }
    122 
     143            return "Unknown (" + distanceRef + ")";
     144        }
     145    }
     146
     147    @Nullable
    123148    public String getGpsDirectionDescription(int tagType)
    124149    {
    125         if (!_directory.containsTag(tagType)) return null;
    126         String gpsDirection = _directory.getString(tagType).trim();
    127         return gpsDirection + " degrees";
    128     }
    129 
     150        Rational angle = _directory.getRational(tagType);
     151        // provide a decimal version of rational numbers in the description, to avoid strings like "35334/199 degrees"
     152        String value = angle != null
     153                ? new DecimalFormat("0.##").format(angle.doubleValue())
     154                : _directory.getString(tagType);
     155        if (value==null || value.trim().length()==0)
     156            return null;
     157        return value.trim() + " degrees";
     158    }
     159
     160    @Nullable
    130161    public String getGpsDirectionReferenceDescription(int tagType)
    131162    {
    132         if (!_directory.containsTag(tagType)) return null;
    133         String gpsDistRef = _directory.getString(tagType).trim();
     163        final String value = _directory.getString(tagType);
     164        if (value==null)
     165            return null;
     166        String gpsDistRef = value.trim();
    134167        if ("T".equalsIgnoreCase(gpsDistRef)) {
    135168            return "True direction";
     
    141174    }
    142175
     176    @Nullable
    143177    public String getGpsSpeedRefDescription()
    144178    {
    145         if (!_directory.containsTag(GpsDirectory.TAG_GPS_SPEED_REF)) return null;
    146         String gpsSpeedRef = _directory.getString(GpsDirectory.TAG_GPS_SPEED_REF).trim();
     179        final String value = _directory.getString(GpsDirectory.TAG_GPS_SPEED_REF);
     180        if (value==null)
     181            return null;
     182        String gpsSpeedRef = value.trim();
    147183        if ("K".equalsIgnoreCase(gpsSpeedRef)) {
    148184            return "kph";
     
    156192    }
    157193
     194    @Nullable
    158195    public String getGpsMeasureModeDescription()
    159196    {
    160         if (!_directory.containsTag(GpsDirectory.TAG_GPS_MEASURE_MODE)) return null;
    161         String gpsSpeedMeasureMode = _directory.getString(GpsDirectory.TAG_GPS_MEASURE_MODE).trim();
     197        final String value = _directory.getString(GpsDirectory.TAG_GPS_MEASURE_MODE);
     198        if (value==null)
     199            return null;
     200        String gpsSpeedMeasureMode = value.trim();
    162201        if ("2".equalsIgnoreCase(gpsSpeedMeasureMode)) {
    163202            return "2-dimensional measurement";
     
    169208    }
    170209
     210    @Nullable
    171211    public String getGpsStatusDescription()
    172212    {
    173         if (!_directory.containsTag(GpsDirectory.TAG_GPS_STATUS)) return null;
    174         String gpsStatus = _directory.getString(GpsDirectory.TAG_GPS_STATUS).trim();
     213        final String value = _directory.getString(GpsDirectory.TAG_GPS_STATUS);
     214        if (value==null)
     215            return null;
     216        String gpsStatus = value.trim();
    175217        if ("A".equalsIgnoreCase(gpsStatus)) {
    176             return "Measurement in progess";
     218            return "Active (Measurement in progress)";
    177219        } else if ("V".equalsIgnoreCase(gpsStatus)) {
    178             return "Measurement Interoperability";
     220            return "Void (Measurement Interoperability)";
    179221        } else {
    180222            return "Unknown (" + gpsStatus + ")";
     
    182224    }
    183225
    184     public String getGpsAltitudeRefDescription() throws MetadataException
    185     {