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


Ignore:
Timestamp:
2013-08-09T18:05:11+02:00 (11 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     {
    186         if (!_directory.containsTag(GpsDirectory.TAG_GPS_ALTITUDE_REF)) return null;
    187         int alititudeRef = _directory.getInt(GpsDirectory.TAG_GPS_ALTITUDE_REF);
    188         if (alititudeRef == 0) {
     226    @Nullable
     227    public String getGpsAltitudeRefDescription()
     228    {
     229        Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_ALTITUDE_REF);
     230        if (value==null)
     231            return null;
     232        if (value == 0)
    189233            return "Sea level";
    190         } else {
    191             return "Unknown (" + alititudeRef + ")";
    192         }
    193     }
    194 
    195     public String getGpsAltitudeDescription() throws MetadataException
    196     {
    197         if (!_directory.containsTag(GpsDirectory.TAG_GPS_ALTITUDE)) return null;
    198         String alititude = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE).toSimpleString(true);
    199         return alititude + " metres";
     234        if (value == 1)
     235            return "Below sea level";
     236        return "Unknown (" + value + ")";
     237    }
     238
     239    @Nullable
     240    public String getGpsAltitudeDescription()
     241    {
     242        final Rational value = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE);
     243        if (value==null)
     244            return null;
     245        return value.intValue() + " metres";
     246    }
     247
     248    @Nullable
     249    public String getGpsDifferentialDescription()
     250    {
     251        final Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_DIFFERENTIAL);
     252        if (value==null)
     253            return null;
     254        if (value == 0)
     255            return "No Correction";
     256        if (value == 1)
     257            return "Differential Corrected";
     258        return "Unknown (" + value + ")";
     259    }
     260
     261    @Nullable
     262    public String getDegreesMinutesSecondsDescription()
     263    {
     264        GeoLocation location = _directory.getGeoLocation();
     265
     266        if (location == null)
     267            return null;
     268
     269        return location.toDMSString();
    200270    }
    201271}
  • trunk/src/com/drew/metadata/exif/GpsDirectory.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 11:00:52 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.GeoLocation;
     24import com.drew.lang.Rational;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
    1927import com.drew.metadata.Directory;
    2028
     
    2230
    2331/**
     32 * Describes Exif tags that contain Global Positioning System (GPS) data.
    2433 *
     34 * @author Drew Noakes http://drewnoakes.com
    2535 */
    2636public class GpsDirectory extends Directory
     
    8191    public static final int TAG_GPS_DEST_DISTANCE = 0x001A;
    8292
    83     protected static final HashMap tagNameMap = new HashMap();
     93    /** Values of "GPS", "CELLID", "WLAN" or "MANUAL" by the EXIF spec. */
     94    public static final int TAG_GPS_PROCESSING_METHOD = 0x001B;
     95    public static final int TAG_GPS_AREA_INFORMATION = 0x001C;
     96    public static final int TAG_GPS_DATE_STAMP = 0x001D;
     97    public static final int TAG_GPS_DIFFERENTIAL = 0x001E;
     98
     99    @NotNull
     100    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    84101
    85102    static
    86103    {
    87         tagNameMap.put(new Integer(TAG_GPS_VERSION_ID), "GPS Version ID");
    88         tagNameMap.put(new Integer(TAG_GPS_LATITUDE_REF), "GPS Latitude Ref");
    89         tagNameMap.put(new Integer(TAG_GPS_LATITUDE), "GPS Latitude");
    90         tagNameMap.put(new Integer(TAG_GPS_LONGITUDE_REF), "GPS Longitude Ref");
    91         tagNameMap.put(new Integer(TAG_GPS_LONGITUDE), "GPS Longitude");
    92         tagNameMap.put(new Integer(TAG_GPS_ALTITUDE_REF), "GPS Altitude Ref");
    93         tagNameMap.put(new Integer(TAG_GPS_ALTITUDE), "GPS Altitude");
    94         tagNameMap.put(new Integer(TAG_GPS_TIME_STAMP), "GPS Time-Stamp");
    95         tagNameMap.put(new Integer(TAG_GPS_SATELLITES), "GPS Satellites");
    96         tagNameMap.put(new Integer(TAG_GPS_STATUS), "GPS Status");
    97         tagNameMap.put(new Integer(TAG_GPS_MEASURE_MODE), "GPS Measure Mode");
    98         tagNameMap.put(new Integer(TAG_GPS_DOP), "GPS DOP");
    99         tagNameMap.put(new Integer(TAG_GPS_SPEED_REF), "GPS Speed Ref");
    100         tagNameMap.put(new Integer(TAG_GPS_SPEED), "GPS Speed");
    101         tagNameMap.put(new Integer(TAG_GPS_TRACK_REF), "GPS Track Ref");
    102         tagNameMap.put(new Integer(TAG_GPS_TRACK), "GPS Track");
    103         tagNameMap.put(new Integer(TAG_GPS_IMG_DIRECTION_REF), "GPS Img Direction Ref");
    104         tagNameMap.put(new Integer(TAG_GPS_IMG_DIRECTION_REF), "GPS Img Direction");
    105         tagNameMap.put(new Integer(TAG_GPS_MAP_DATUM), "GPS Map Datum");
    106         tagNameMap.put(new Integer(TAG_GPS_DEST_LATITUDE_REF), "GPS Dest Latitude Ref");
    107         tagNameMap.put(new Integer(TAG_GPS_DEST_LATITUDE), "GPS Dest Latitude");
    108         tagNameMap.put(new Integer(TAG_GPS_DEST_LONGITUDE_REF), "GPS Dest Longitude Ref");
    109         tagNameMap.put(new Integer(TAG_GPS_DEST_LONGITUDE), "GPS Dest Longitude");
    110         tagNameMap.put(new Integer(TAG_GPS_DEST_BEARING_REF), "GPS Dest Bearing Ref");
    111         tagNameMap.put(new Integer(TAG_GPS_DEST_BEARING), "GPS Dest Bearing");
    112         tagNameMap.put(new Integer(TAG_GPS_DEST_DISTANCE_REF), "GPS Dest Distance Ref");
    113         tagNameMap.put(new Integer(TAG_GPS_DEST_DISTANCE), "GPS Dest Distance");
     104        _tagNameMap.put(TAG_GPS_VERSION_ID, "GPS Version ID");
     105        _tagNameMap.put(TAG_GPS_LATITUDE_REF, "GPS Latitude Ref");
     106        _tagNameMap.put(TAG_GPS_LATITUDE, "GPS Latitude");
     107        _tagNameMap.put(TAG_GPS_LONGITUDE_REF, "GPS Longitude Ref");
     108        _tagNameMap.put(TAG_GPS_LONGITUDE, "GPS Longitude");
     109        _tagNameMap.put(TAG_GPS_ALTITUDE_REF, "GPS Altitude Ref");
     110        _tagNameMap.put(TAG_GPS_ALTITUDE, "GPS Altitude");
     111        _tagNameMap.put(TAG_GPS_TIME_STAMP, "GPS Time-Stamp");
     112        _tagNameMap.put(TAG_GPS_SATELLITES, "GPS Satellites");
     113        _tagNameMap.put(TAG_GPS_STATUS, "GPS Status");
     114        _tagNameMap.put(TAG_GPS_MEASURE_MODE, "GPS Measure Mode");
     115        _tagNameMap.put(TAG_GPS_DOP, "GPS DOP");
     116        _tagNameMap.put(TAG_GPS_SPEED_REF, "GPS Speed Ref");
     117        _tagNameMap.put(TAG_GPS_SPEED, "GPS Speed");
     118        _tagNameMap.put(TAG_GPS_TRACK_REF, "GPS Track Ref");
     119        _tagNameMap.put(TAG_GPS_TRACK, "GPS Track");
     120        _tagNameMap.put(TAG_GPS_IMG_DIRECTION_REF, "GPS Img Direction Ref");
     121        _tagNameMap.put(TAG_GPS_IMG_DIRECTION, "GPS Img Direction");
     122        _tagNameMap.put(TAG_GPS_MAP_DATUM, "GPS Map Datum");
     123        _tagNameMap.put(TAG_GPS_DEST_LATITUDE_REF, "GPS Dest Latitude Ref");
     124        _tagNameMap.put(TAG_GPS_DEST_LATITUDE, "GPS Dest Latitude");
     125        _tagNameMap.put(TAG_GPS_DEST_LONGITUDE_REF, "GPS Dest Longitude Ref");
     126        _tagNameMap.put(TAG_GPS_DEST_LONGITUDE, "GPS Dest Longitude");
     127        _tagNameMap.put(TAG_GPS_DEST_BEARING_REF, "GPS Dest Bearing Ref");
     128        _tagNameMap.put(TAG_GPS_DEST_BEARING, "GPS Dest Bearing");
     129        _tagNameMap.put(TAG_GPS_DEST_DISTANCE_REF, "GPS Dest Distance Ref");
     130        _tagNameMap.put(TAG_GPS_DEST_DISTANCE, "GPS Dest Distance");
     131        _tagNameMap.put(TAG_GPS_PROCESSING_METHOD, "GPS Processing Method");
     132        _tagNameMap.put(TAG_GPS_AREA_INFORMATION, "GPS Area Information");
     133        _tagNameMap.put(TAG_GPS_DATE_STAMP, "GPS Date Stamp");
     134        _tagNameMap.put(TAG_GPS_DIFFERENTIAL, "GPS Differential");
    114135    }
    115136
     
    119140    }
    120141
     142    @NotNull
    121143    public String getName()
    122144    {
     
    124146    }
    125147
    126     protected HashMap getTagNameMap()
     148    @NotNull
     149    protected HashMap<Integer, String> getTagNameMap()
    127150    {
    128         return tagNameMap;
     151        return _tagNameMap;
     152    }
     153
     154    /**
     155     * Parses various tags in an attempt to obtain a single object representing the latitude and longitude
     156     * at which this image was captured.
     157     *
     158     * @return The geographical location of this image, if possible, otherwise null
     159     */
     160    @Nullable
     161    public GeoLocation getGeoLocation()
     162    {
     163        Rational[] latitudes = getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
     164        Rational[] longitudes = getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
     165        String latitudeRef = getString(GpsDirectory.TAG_GPS_LATITUDE_REF);
     166        String longitudeRef = getString(GpsDirectory.TAG_GPS_LONGITUDE_REF);
     167
     168        // Make sure we have the required values
     169        if (latitudes == null || latitudes.length != 3)
     170            return null;
     171        if (longitudes == null || longitudes.length != 3)
     172            return null;
     173        if (latitudeRef == null || longitudeRef == null)
     174            return null;
     175
     176        Double lat = GeoLocation.degreesMinutesSecondsToDecimal(latitudes[0], latitudes[1], latitudes[2], latitudeRef.equalsIgnoreCase("S"));
     177        Double lon = GeoLocation.degreesMinutesSecondsToDecimal(longitudes[0], longitudes[1], longitudes[2], longitudeRef.equalsIgnoreCase("W"));
     178
     179        // This can return null, in cases where the conversion was not possible
     180        if (lat == null || lon == null)
     181            return null;
     182
     183        return new GeoLocation(lat, lon);
    129184    }
    130185}
  • trunk/src/com/drew/metadata/exif/KodakMakernoteDescriptor.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
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.TagDescriptor;
    2025
    2126/**
    22  * Provides human-readable string versions of the tags stored in a KodakMakernoteDirectory.
     27 * Provides human-readable string representations of tag values stored in a <code>KodakMakernoteDirectory</code>.
     28 *
    2329 * Thanks to David Carson for the initial version of this class.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
    2432 */
    25 public class KodakMakernoteDescriptor extends TagDescriptor
     33public class KodakMakernoteDescriptor extends TagDescriptor<KodakMakernoteDirectory>
    2634{
    27         public KodakMakernoteDescriptor(Directory directory)
    28         {
    29                 super(directory);
    30         }
    31        
    32         public String getDescription(int tagType) throws MetadataException
     35    public KodakMakernoteDescriptor(@NotNull KodakMakernoteDirectory directory)
    3336    {
    34                 return _directory.getString(tagType);
    35         }
     37        super(directory);
     38    }
    3639}
  • trunk/src/com/drew/metadata/exif/KodakMakernoteDirectory.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.Directory;
    1825
     
    2128/**
    2229 * Describes tags specific to Kodak cameras.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
    2332 */
    2433public class KodakMakernoteDirectory extends Directory
    2534{
    26         protected static final HashMap _tagNameMap = new HashMap();
    27        
    28         public String getName()
     35    @NotNull
     36    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     37
     38    public KodakMakernoteDirectory()
    2939    {
    30                 return "Kodak Makernote";
    31         }
     40        this.setDescriptor(new KodakMakernoteDescriptor(this));
     41    }
    3242
    33         protected HashMap getTagNameMap()
     43    @NotNull
     44    public String getName()
    3445    {
    35                 return _tagNameMap;
    36         }
     46        return "Kodak Makernote";
     47    }
     48
     49    @NotNull
     50    protected HashMap<Integer, String> getTagNameMap()
     51    {
     52        return _tagNameMap;
     53    }
    3754}
  • trunk/src/com/drew/metadata/exif/KyoceraMakernoteDescriptor.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
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
    2127/**
    22  * Provides human-readable string versions of the tags stored in a KyoceraMakernoteDirectory.
    23  *
     28 * Provides human-readable string representations of tag values stored in a <code>KyoceraMakernoteDirectory</code>.
     29 * <p/>
    2430 * Some information about this makernote taken from here:
    2531 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
    26  *
     32 * <p/>
    2733 * Most manufacturer's MakerNote counts the "offset to data" from the first byte
    2834 * of TIFF header (same as the other IFD), but Kyocera (along with Fujifilm) counts
    2935 * it from the first byte of MakerNote itself.
     36 *
     37 * @author Drew Noakes http://drewnoakes.com
    3038 */
    31 public class KyoceraMakernoteDescriptor extends TagDescriptor
     39public class KyoceraMakernoteDescriptor extends TagDescriptor<KyoceraMakernoteDirectory>
    3240{
    33     public KyoceraMakernoteDescriptor(Directory directory)
     41    public KyoceraMakernoteDescriptor(@NotNull KyoceraMakernoteDirectory directory)
    3442    {
    3543        super(directory);
    3644    }
    3745
    38     public String getDescription(int tagType) throws MetadataException
     46    @Nullable
     47    public String getDescription(int tagType)
    3948    {
    4049        switch (tagType) {
     
    4453                return getProprietaryThumbnailDataDescription();
    4554            default:
    46                 return _directory.getString(tagType);
     55                return super.getDescription(tagType);
    4756        }
    4857    }
    4958
    50     public String getPrintImageMatchingInfoDescription() throws MetadataException
     59    @Nullable
     60    public String getPrintImageMatchingInfoDescription()
    5161    {
    52         if (!_directory.containsTag(KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO)) return null;
    5362        byte[] bytes = _directory.getByteArray(KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO);
     63        if (bytes==null)
     64            return null;
    5465        return "(" + bytes.length + " bytes)";
    5566    }
    5667
    57     public String getProprietaryThumbnailDataDescription() throws MetadataException
     68    @Nullable
     69    public String getProprietaryThumbnailDataDescription()
    5870    {
    59         if (!_directory.containsTag(KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL)) return null;
    6071        byte[] bytes = _directory.getByteArray(KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL);
     72        if (bytes==null)
     73            return null;
    6174        return "(" + bytes.length + " bytes)";
    6275    }
  • trunk/src/com/drew/metadata/exif/KyoceraMakernoteDirectory.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 Kyocera and Contax cameras.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class KyoceraMakernoteDirectory extends Directory
     
    2936    public static final int TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    3037
    31     protected static final HashMap tagNameMap = new HashMap();
     38    @NotNull
     39    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    3240
    3341    static
    3442    {
    35         tagNameMap.put(new Integer(TAG_KYOCERA_PROPRIETARY_THUMBNAIL), "Proprietary Thumbnail Format Data");
    36         tagNameMap.put(new Integer(TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");
     43        _tagNameMap.put(TAG_KYOCERA_PROPRIETARY_THUMBNAIL, "Proprietary Thumbnail Format Data");
     44        _tagNameMap.put(TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
    3745    }
    3846
     
    4250    }
    4351
     52    @NotNull
    4453    public String getName()
    4554    {
     
    4756    }
    4857
    49     protected HashMap getTagNameMap()
     58    @NotNull
     59    protected HashMap<Integer, String> getTagNameMap()
    5060    {
    51         return tagNameMap;
     61        return _tagNameMap;
    5262    }
    5363}
  • trunk/src/com/drew/metadata/exif/NikonType1MakernoteDescriptor.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/
     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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    1723import com.drew.lang.Rational;
    18 import com.drew.metadata.Directory;
    19 import com.drew.metadata.MetadataException;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
    2026import com.drew.metadata.TagDescriptor;
    2127
    2228/**
    23  * Provides human-readable string versions of the tags stored in a NikonType1MakernoteDirectory.
     29 * Provides human-readable string representations of tag values stored in a <code>NikonType1MakernoteDirectory</code>.
     30 * <p/>
    2431 * Type-1 is for E-Series cameras prior to (not including) E990.  For example: E700, E800, E900,
    2532 * E900S, E910, E950.
    26  *
     33 * <p/>
    2734 * MakerNote starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from
    2835 * offset 0x08. This is the same as Olympus except start string. Example of actual data
     
    3239 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
    3340 * </code></pre>
     41 *
     42 * @author Drew Noakes http://drewnoakes.com
    3443 */
    35 public class NikonType1MakernoteDescriptor extends TagDescriptor
     44public class NikonType1MakernoteDescriptor extends TagDescriptor<NikonType1MakernoteDirectory>
    3645{
    37     public NikonType1MakernoteDescriptor(Directory directory)
     46    public NikonType1MakernoteDescriptor(@NotNull NikonType1MakernoteDirectory directory)
    3847    {
    3948        super(directory);
    4049    }
    4150
    42     public String getDescription(int tagType) throws MetadataException
     51    @Nullable
     52    public String getDescription(int tagType)
    4353    {
    4454        switch (tagType) {
     
    6070                return getConverterDescription();
    6171            default:
    62                 return _directory.getString(tagType);
    63         }
    64     }
    65 
    66     public String getConverterDescription() throws MetadataException
    67     {
    68         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER)) return null;
    69         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER);
     72                return super.getDescription(tagType);
     73        }
     74    }
     75
     76    @Nullable
     77    public String getConverterDescription()
     78    {
     79        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER);
     80        if (value == null)
     81            return null;
    7082        switch (value) {
    7183            case 0:
     
    7890    }
    7991
    80     public String getDigitalZoomDescription() throws MetadataException
    81     {
    82         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM)) return null;
     92    @Nullable
     93    public String getDigitalZoomDescription()
     94    {
    8395        Rational value = _directory.getRational(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM);
     96        if (value == null)
     97            return null;
    8498        if (value.getNumerator() == 0) {
    8599            return "No digital zoom";
     
    88102    }
    89103
    90     public String getFocusDescription() throws MetadataException
    91     {
    92         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS)) return null;
     104    @Nullable
     105    public String getFocusDescription()
     106    {
    93107        Rational value = _directory.getRational(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS);
     108        if (value == null)
     109            return null;
    94110        if (value.getNumerator() == 1 && value.getDenominator() == 0) {
    95111            return "Infinite";
     
    98114    }
    99115
    100     public String getWhiteBalanceDescription() throws MetadataException
    101     {
    102         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE)) return null;
    103         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE);
     116    @Nullable
     117    public String getWhiteBalanceDescription()
     118    {
     119        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE);
     120        if (value == null)
     121            return null;
    104122        switch (value) {
    105123            case 0:
     
    110128                return "Daylight";
    111129            case 3:
    112                 return "Incandescense";
    113             case 4:
    114                 return "Flourescence";
     130                return "Incandescence";
     131            case 4:
     132                return "Florescence";
    115133            case 5:
    116134                return "Cloudy";
     
    122140    }
    123141
    124     public String getCcdSensitivityDescription() throws MetadataException
    125     {
    126         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY)) return null;
    127         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY);
     142    @Nullable
     143    public String getCcdSensitivityDescription()
     144    {
     145        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY);
     146        if (value == null)
     147            return null;
    128148        switch (value) {
    129149            case 0:
     
    140160    }
    141161
    142     public String getImageAdjustmentDescription() throws MetadataException
    143     {
    144         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT)) return null;
    145         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT);
     162    @Nullable
     163    public String getImageAdjustmentDescription()
     164    {
     165        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT);
     166        if (value == null)
     167            return null;
    146168        switch (value) {
    147169            case 0:
     
    160182    }
    161183
    162     public String getColorModeDescription() throws MetadataException
    163     {
    164         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE)) return null;
    165         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE);
     184    @Nullable
     185    public String getColorModeDescription()
     186    {
     187        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE);
     188        if (value == null)
     189            return null;
    166190        switch (value) {
    167191            case 1:
     
    174198    }
    175199
    176     public String getQualityDescription() throws MetadataException
    177     {
    178         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY)) return null;
    179         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY);
     200    @Nullable
     201    public String getQualityDescription()
     202    {
     203        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY);
     204        if (value == null)
     205            return null;
    180206        switch (value) {
    181207            case 1:
  • trunk/src/com/drew/metadata/exif/NikonType1MakernoteDirectory.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/**
    24  * Contains values specific to Nikon cameras.  Type-1 is for E-Series cameras prior to (not including) E990.
     29 * Describes tags specific to Nikon (type 1) cameras.  Type-1 is for E-Series cameras prior to (not including) E990.
    2530 *
    2631 * There are 3 formats of Nikon's MakerNote. MakerNote of E700/E800/E900/E900S/E910/E950
     
    3237 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
    3338 * </code></pre>
     39 *
     40 * @author Drew Noakes http://drewnoakes.com
    3441 */
    3542public class NikonType1MakernoteDirectory extends Directory
     
    4754    public static final int TAG_NIKON_TYPE1_UNKNOWN_3 = 0x0F00;
    4855
    49     protected static final HashMap _tagNameMap = new HashMap();
     56    @NotNull
     57    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    5058
    5159    static
    5260    {
    53         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_CCD_SENSITIVITY), "CCD Sensitivity");
    54         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_COLOR_MODE), "Color Mode");
    55         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_DIGITAL_ZOOM), "Digital Zoom");
    56         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_CONVERTER), "Fisheye Converter");
    57         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_FOCUS), "Focus");
    58         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT), "Image Adjustment");
    59         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_QUALITY), "Quality");
    60         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_UNKNOWN_1), "Makernote Unknown 1");
    61         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_UNKNOWN_2), "Makernote Unknown 2");
    62         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_UNKNOWN_3), "Makernote Unknown 3");
    63         _tagNameMap.put(new Integer(TAG_NIKON_TYPE1_WHITE_BALANCE), "White Balance");
     61        _tagNameMap.put(TAG_NIKON_TYPE1_CCD_SENSITIVITY, "CCD Sensitivity");
     62        _tagNameMap.put(TAG_NIKON_TYPE1_COLOR_MODE, "Color Mode");
     63        _tagNameMap.put(TAG_NIKON_TYPE1_DIGITAL_ZOOM, "Digital Zoom");
     64        _tagNameMap.put(TAG_NIKON_TYPE1_CONVERTER, "Fisheye Converter");
     65        _tagNameMap.put(TAG_NIKON_TYPE1_FOCUS, "Focus");
     66        _tagNameMap.put(TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT, "Image Adjustment");
     67        _tagNameMap.put(TAG_NIKON_TYPE1_QUALITY, "Quality");
     68        _tagNameMap.put(TAG_NIKON_TYPE1_UNKNOWN_1, "Makernote Unknown 1");
     69        _tagNameMap.put(TAG_NIKON_TYPE1_UNKNOWN_2, "Makernote Unknown 2");
     70        _tagNameMap.put(TAG_NIKON_TYPE1_UNKNOWN_3, "Makernote Unknown 3");
     71        _tagNameMap.put(TAG_NIKON_TYPE1_WHITE_BALANCE, "White Balance");
    6472    }
    6573
     
    6977    }
    7078
     79    @NotNull
    7180    public String getName()
    7281    {
     
    7483    }
    7584
    76     protected HashMap getTagNameMap()
     85    @NotNull
     86    protected HashMap<Integer, String> getTagNameMap()
    7787    {
    7888        return _tagNameMap;
  • trunk/src/com/drew/metadata/exif/NikonType2MakernoteDescriptor.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/
     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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    1723import com.drew.lang.Rational;
    18 import com.drew.metadata.Directory;
    19 import com.drew.metadata.MetadataException;
     24import com.drew.lang.StringUtil;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
    2027import com.drew.metadata.TagDescriptor;
    2128
    2229import java.text.DecimalFormat;
     30import java.util.ArrayList;
     31import java.util.Collection;
     32import java.util.Date;
    2333
    2434/**
    25  * Provides human-readable string versions of the tags stored in a NikonType2MakernoteDirectory.
     35 * Provides human-readable string representations of tag values stored in a <code>NikonType2MakernoteDirectory</code>.
     36 *
    2637 * Type-2 applies to the E990 and D-series cameras such as the D1, D70 and D100.
     38 *
     39 * @author Drew Noakes http://drewnoakes.com
    2740 */
    28 public class NikonType2MakernoteDescriptor extends TagDescriptor
     41public class NikonType2MakernoteDescriptor extends TagDescriptor<NikonType2MakernoteDirectory>
    2942{
    30     public NikonType2MakernoteDescriptor(Directory directory)
     43    public NikonType2MakernoteDescriptor(@NotNull NikonType2MakernoteDirectory directory)
    3144    {
    3245        super(directory);
    3346    }
    3447
    35     private NikonType2MakernoteDirectory getMakernoteDirectory()
    36     {
    37         return (NikonType2MakernoteDirectory)_directory;
    38     }
    39 
    40     public String getDescription(int tagType) throws MetadataException
     48    @Nullable
     49    public String getDescription(int tagType)
    4150    {
    4251        switch (tagType)
    4352        {
     53            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT:
     54                return getProgramShiftDescription();
     55            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE:
     56                return getExposureDifferenceDescription();
    4457            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS:
    4558                return getLensDescription();
     
    5063            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION:
    5164                return getAutoFlashCompensationDescription();
     65            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION:
     66                return getFlashExposureCompensationDescription();
     67            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION:
     68                return getFlashBracketCompensationDescription();
     69            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_TUNING:
     70                return getExposureTuningDescription();
     71            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_STOPS:
     72                return getLensStopsDescription();
     73            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_COLOR_SPACE:
     74                return getColorSpaceDescription();
     75            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING:
     76                return getActiveDLightingDescription();
     77            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_VIGNETTE_CONTROL:
     78                return getVignetteControlDescription();
    5279            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1:
    5380                return getIsoSettingDescription();
    5481            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM:
    5582                return getDigitalZoomDescription();
     83            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED:
     84                return getFlashUsedDescription();
    5685            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION:
    5786                return getAutoFocusPositionDescription();
    5887            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION:
    59                 return getAutoFirmwareVersionDescription();
     88                return getFirmwareVersionDescription();
     89            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_TYPE:
     90                return getLensTypeDescription();
     91            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_SHOOTING_MODE:
     92                return getShootingModeDescription();
     93            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_NEF_COMPRESSION:
     94                return getNEFCompressionDescription();
     95            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION:
     96                return getHighISONoiseReductionDescription();
     97            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_POWER_UP_TIME:
     98                return getPowerUpTimeDescription();
    6099            default:
    61                 return _directory.getString(tagType);
    62         }
    63     }
    64 
    65     public String getAutoFocusPositionDescription() throws MetadataException
    66     {
    67         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION)) return null;
     100                return super.getDescription(tagType);
     101        }
     102    }
     103
     104    @Nullable
     105    public String getPowerUpTimeDescription()
     106    {
     107        Long value = _directory.getLongObject(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_POWER_UP_TIME);
     108        if (value==null)
     109            return null; // TODO have observed a byte[8] here which is likely some kind of date (ticks as long?)
     110        return new Date(value).toString();
     111    }
     112
     113    @Nullable
     114    public String getHighISONoiseReductionDescription()
     115    {
     116        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION);
     117        if (value==null)
     118            return null;
     119        switch (value) {
     120            case 0: return "Off";
     121            case 1: return "Minimal";
     122            case 2: return "Low";
     123            case 4: return "Normal";
     124            case 6: return "High";
     125            default: return "Unknown (" + value + ")";
     126        }
     127    }
     128
     129    @Nullable
     130    public String getFlashUsedDescription()
     131    {
     132        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED);
     133        if (value==null)
     134            return null;
     135        switch (value) {
     136            case 0: return "Flash Not Used";
     137            case 1: return "Manual Flash";
     138            case 3: return "Flash Not Ready";
     139            case 7: return "External Flash";
     140            case 8: return "Fired, Commander Mode";
     141            case 9: return "Fired, TTL Mode";
     142            default: return "Unknown (" + value + ")";
     143        }
     144    }
     145
     146    @Nullable
     147    public String getNEFCompressionDescription()
     148    {
     149        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_NEF_COMPRESSION);
     150        if (value==null)
     151            return null;
     152        switch (value) {
     153            case 1: return "Lossy (Type 1)";
     154            case 3: return "Uncompressed";
     155            case 7: return "Lossless";
     156            case 8: return "Lossy (Type 2)";
     157            default: return "Unknown (" + value + ")";
     158        }
     159    }
     160
     161    @Nullable
     162    public String getShootingModeDescription()
     163    {
     164        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_SHOOTING_MODE);
     165        if (value==null)
     166            return null;
     167        Collection<String> bits = new ArrayList<String>();
     168
     169        if ((value&1)==1)
     170            bits.add("Continuous");
     171        else
     172            bits.add("Single Frame");
     173
     174        if ((value&2)==2)
     175            bits.add("Delay");
     176        // Don't know about 3
     177        if ((value&8)==8)
     178            bits.add("PC Control");
     179        if ((value&16)==16)
     180            bits.add("Exposure Bracketing");
     181        if ((value&32)==32)
     182            bits.add("Auto ISO");
     183        if ((value&64)==64)
     184            bits.add("White-Balance Bracketing");
     185        if ((value&128)==128)
     186            bits.add("IR Control");
     187
     188        return StringUtil.join(bits, ", ");
     189    }
     190
     191    @Nullable
     192    public String getLensTypeDescription()
     193    {
     194        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_TYPE);
     195        if (value==null)
     196            return null;
     197
     198        Collection<String> bits = new ArrayList<String>();
     199
     200        // TODO validate these values, as 14 is labelled as AF-C elsewhere but appears here as AF-D-G-VR
     201
     202        if ((value&1)==1)
     203            bits.add("MF");
     204        else
     205            bits.add("AF");
     206
     207        if ((value&2)==2)
     208            bits.add("D");
     209
     210        if ((value&4)==4)
     211            bits.add("G");
     212
     213        if ((value&8)==8)
     214            bits.add("VR");
     215
     216        return StringUtil.join(bits, ", ");
     217    }
     218
     219    @Nullable
     220    public String getColorSpaceDescription()
     221    {
     222        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_COLOR_SPACE);
     223        if (value==null)
     224            return null;
     225        switch (value) {
     226            case 1: return "sRGB";
     227            case 2: return "Adobe RGB";
     228            default: return "Unknown (" + value + ")";
     229        }
     230    }
     231
     232    @Nullable
     233    public String getActiveDLightingDescription()
     234    {
     235        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING);
     236        if (value==null)
     237            return null;
     238        switch (value) {
     239            case 0: return "Off";
     240            case 1: return "Light";
     241            case 3: return "Normal";
     242            case 5: return "High";
     243            case 7: return "Extra High";
     244            case 65535: return "Auto";
     245            default: return "Unknown (" + value + ")";
     246        }
     247    }
     248
     249    @Nullable
     250    public String getVignetteControlDescription()
     251    {
     252        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_VIGNETTE_CONTROL);
     253        if (value==null)
     254            return null;
     255        switch (value) {
     256            case 0: return "Off";
     257            case 1: return "Low";
     258            case 3: return "Normal";
     259            case 5: return "High";
     260            default: return "Unknown (" + value + ")";
     261        }
     262    }
     263
     264    @Nullable
     265    public String getAutoFocusPositionDescription()
     266    {
    68267        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION);
     268        if (values==null)
     269            return null;
    69270        if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) {
    70271            return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION) + ")";
     
    86287    }
    87288
    88     public String getDigitalZoomDescription() throws MetadataException
    89     {
    90         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM)) return null;
    91         Rational rational = _directory.getRational(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM);
    92         if (rational.intValue() == 1) {
    93             return "No digital zoom";
    94         }
    95         return rational.toSimpleString(true) + "x digital zoom";
    96     }
    97 
    98     public String getIsoSettingDescription() throws MetadataException
    99     {
    100         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1)) return null;
     289    @Nullable
     290    public String getDigitalZoomDescription()
     291    {
     292        Rational value = _directory.getRational(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM);
     293        if (value==null)
     294            return null;
     295        return value.intValue() == 1
     296                ? "No digital zoom"
     297                : value.toSimpleString(true) + "x digital zoom";
     298    }
     299
     300    @Nullable
     301    public String getProgramShiftDescription()
     302    {
     303        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT);
     304        return getEVDescription(values);
     305    }
     306
     307    @Nullable
     308    public String getExposureDifferenceDescription()
     309    {
     310        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE);
     311        return getEVDescription(values);
     312    }
     313
     314    @NotNull
     315    public String getAutoFlashCompensationDescription()
     316    {
     317        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION);
     318        return getEVDescription(values);
     319    }
     320
     321    @NotNull
     322    public String getFlashExposureCompensationDescription()
     323    {
     324        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION);
     325        return getEVDescription(values);
     326    }
     327
     328    @NotNull
     329    public String getFlashBracketCompensationDescription()
     330    {
     331        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION);
     332        return getEVDescription(values);
     333    }
     334
     335    @NotNull
     336    public String getExposureTuningDescription()
     337    {
     338        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_TUNING);
     339        return getEVDescription(values);
     340    }
     341
     342    @NotNull
     343    public String getLensStopsDescription()
     344    {
     345        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_STOPS);
     346        return getEVDescription(values);
     347    }
     348
     349    @Nullable
     350    private static String getEVDescription(@Nullable int[] values)
     351    {
     352        if (values==null)
     353            return null;
     354        if (values.length<3 || values[2]==0)
     355            return null;
     356        final DecimalFormat decimalFormat = new DecimalFormat("0.##");
     357        double ev = values[0] * values[1] / (double)values[2];
     358        return decimalFormat.format(ev) + " EV";
     359    }
     360
     361    @Nullable
     362    public String getIsoSettingDescription()
     363    {
    101364        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1);
    102         if (values[0] != 0 || values[1] == 0) {
     365        if (values==null)
     366            return null;
     367        if (values[0] != 0 || values[1] == 0)
    103368            return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1) + ")";
    104         }
    105369        return "ISO " + values[1];
    106370    }
    107371
    108     public String getAutoFlashCompensationDescription() throws MetadataException
    109     {
    110         Rational ev = getMakernoteDirectory().getAutoFlashCompensation();
    111 
    112         if (ev==null)
    113             return "Unknown";
    114 
    115         DecimalFormat decimalFormat = new DecimalFormat("0.##");
    116         return decimalFormat.format(ev.floatValue()) + " EV";
    117     }
    118 
    119     public String getLensDescription() throws MetadataException
    120     {
    121         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS))
    122             return null;
    123 
    124         Rational[] lensValues = _directory.getRationalArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
    125 
    126         if (lensValues.length!=4)
     372    @Nullable
     373    public String getLensDescription()
     374    {
     375        Rational[] values = _directory.getRationalArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
     376
     377        if (values==null)
     378            return null;
     379
     380        if (values.length<4)
    127381            return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
    128382
    129         StringBuffer description = new StringBuffer();
    130         description.append(lensValues[0].intValue());
     383        StringBuilder description = new StringBuilder();
     384        description.append(values[0].intValue());
    131385        description.append('-');
    132         description.append(lensValues[1].intValue());
     386        description.append(values[1].intValue());
    133387        description.append("mm f/");
    134         description.append(lensValues[2].floatValue());
     388        description.append(values[2].floatValue());
    135389        description.append('-');
    136         description.append(lensValues[3].floatValue());
     390        description.append(values[3].floatValue());
    137391
    138392        return description.toString();
    139393    }
    140394
     395    @Nullable
    141396    public String getHueAdjustmentDescription()
    142397    {
    143         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT))
    144             return null;
    145 
    146         return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT) + " degrees";
    147     }
    148 
     398        final String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT);
     399        if (value==null)
     400            return null;
     401        return value + " degrees";
     402    }
     403
     404    @Nullable
    149405    public String getColorModeDescription()
    150406    {
    151         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE))
    152             return null;
    153 
    154         String raw = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE);
    155         if (raw.startsWith("MODE1"))
     407        String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE);
     408        if (value==null)
     409            return null;
     410        if (value.startsWith("MODE1"))
    156411            return "Mode I (sRGB)";
    157 
    158         return raw;
    159     }
    160 
    161     public String getAutoFirmwareVersionDescription() throws MetadataException
    162     {
    163         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION))
    164             return null;
    165 
    166         int[] ints = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION);
    167         return ExifDescriptor.convertBytesToVersionString(ints);
     412        return value;
     413    }
     414
     415    @Nullable
     416    public String getFirmwareVersionDescription()
     417    {
     418        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION);
     419        if (values==null)
     420            return null;
     421        return ExifSubIFDDescriptor.convertBytesToVersionString(values, 2);
    168422    }
    169423}
  • trunk/src/com/drew/metadata/exif/NikonType2MakernoteDirectory.java

    r4258 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  *   metadata_extractor [at] drewnoakes [dot] 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 3-Oct-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
    19 import com.drew.lang.Rational;
     23import com.drew.lang.annotations.NotNull;
    2024import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
    2225
    2326import java.util.HashMap;
     
    2629 * Describes tags specific to Nikon (type 2) cameras.  Type-2 applies to the E990 and D-series cameras such as the E990, D1,
    2730 * D70 and D100.
    28  *
     31 * <p/>
    2932 * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data.
    3033 * http://www.timelesswanderings.net/equipment/D100/NEF.html
    31  *
     34 * <p/>
    3235 * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute.  Similarly
    3336 * image hiding uses the 'hidden' attribute (observed on the D70).  Consequently, these values are not available here.
    34  *
     37 * <p/>
    3538 * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field.
    3639 * New tags have subsequently been added since Fabrizio's observations.
    37  *
     40 * <p/>
    3841 * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD.  In
    3942 * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also
    4043 * provide this prefix to aid in software decoding).
     44 *
     45 * @author Drew Noakes http://drewnoakes.com
    4146 */
    4247public class NikonType2MakernoteDirectory extends Directory
     
    5762
    5863    /**
    59      * Values observed
    60      * - COLOR (seen in the D1X)
     64     * The camera's color mode, as an uppercase string.  Examples include:
     65     * <ul>
     66     * <li><code>B & W</code></li>
     67     * <li><code>COLOR</code></li>
     68     * <li><code>COOL</code></li>
     69     * <li><code>SEPIA</code></li>
     70     * <li><code>VIVID</code></li>
     71     * </ul>
    6172     */
    6273    public static final int TAG_NIKON_TYPE2_COLOR_MODE = 0x0003;
    6374
    6475    /**
    65      * Values observed
    66      * - FILE
    67      * - RAW
    68      * - NORMAL
    69      * - FINE
     76     * The camera's quality setting, as an uppercase string.  Examples include:
     77     * <ul>
     78     * <li><code>BASIC</code></li>
     79     * <li><code>FINE</code></li>
     80     * <li><code>NORMAL</code></li>
     81     * <li><code>RAW</code></li>
     82     * <li><code>RAW2.7M</code></li>
     83     * </ul>
    7084     */
    7185    public static final int TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT = 0x0004;
    7286
    7387    /**
    74      * The white balance as set in the camera.
    75      *
    76      * Values observed
    77      * - AUTO
    78      * - SUNNY (D70)
    79      * - FLASH (D1X)
    80      * (presumably also SHADOW / INCANDESCENT / FLUORESCENT / CLOUDY)
     88     * The camera's white balance setting, as an uppercase string.  Examples include:
     89     *
     90     * <ul>
     91     * <li><code>AUTO</code></li>
     92     * <li><code>CLOUDY</code></li>
     93     * <li><code>FLASH</code></li>
     94     * <li><code>FLUORESCENT</code></li>
     95     * <li><code>INCANDESCENT</code></li>
     96     * <li><code>PRESET</code></li>
     97     * <li><code>PRESET0</code></li>
     98     * <li><code>PRESET1</code></li>
     99     * <li><code>PRESET3</code></li>
     100     * <li><code>SUNNY</code></li>
     101     * <li><code>WHITE PRESET</code></li>
     102     * <li><code>4350K</code></li>
     103     * <li><code>5000K</code></li>
     104     * <li><code>DAY WHITE FL</code></li>
     105     * <li><code>SHADE</code></li>
     106     * </ul>
    81107     */
    82108    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE  = 0x0005;
    83109
    84110    /**
    85      * The sharpening as set in the camera.
    86      *
    87      * Values observed
    88      * - AUTO
    89      * - NORMAL (D70)
    90      * - NONE (D1X)
     111     * The camera's sharpening setting, as an uppercase string.  Examples include:
     112     *
     113     * <ul>
     114     * <li><code>AUTO</code></li>
     115     * <li><code>HIGH</code></li>
     116     * <li><code>LOW</code></li>
     117     * <li><code>NONE</code></li>
     118     * <li><code>NORMAL</code></li>
     119     * <li><code>MED.H</code></li>
     120     * <li><code>MED.L</code></li>
     121     * </ul>
    91122     */
    92123    public static final int TAG_NIKON_TYPE2_CAMERA_SHARPENING = 0x0006;
    93124
    94125    /**
    95      * The auto-focus type used by the camera.
    96      *
    97      * Values observed
    98      * - AF-S
    99      * - AF-C
    100      * - MANUAL
     126     * The camera's auto-focus mode, as an uppercase string.  Examples include:
     127     *
     128     * <ul>
     129     * <li><code>AF-C</code></li>
     130     * <li><code>AF-S</code></li>
     131     * <li><code>MANUAL</code></li>
     132     * <li><code>AF-A</code></li>
     133     * </ul>
    101134     */
    102135    public static final int TAG_NIKON_TYPE2_AF_TYPE = 0x0007;
    103136
    104137    /**
    105      * Values observed
    106      * - NORMAL
    107      * - RED-EYE
    108      *
    109      * Note: when TAG_NIKON_TYPE2_AUTO_FLASH_MODE is blank, Nikon Browser displays "Flash Sync Mode: Not Attached"
     138     * The camera's flash setting, as an uppercase string.  Examples include:
     139     *
     140     * <ul>
     141     * <li><code></code></li>
     142     * <li><code>NORMAL</code></li>
     143     * <li><code>RED-EYE</code></li>
     144     * <li><code>SLOW</code></li>
     145     * <li><code>NEW_TTL</code></li>
     146     * <li><code>REAR</code></li>
     147     * <li><code>REAR SLOW</code></li>
     148     * </ul>
     149     * Note: when TAG_NIKON_TYPE2_AUTO_FLASH_MODE is blank (whitespace), Nikon Browser displays "Flash Sync Mode: Not Attached"
    110150     */
    111151    public static final int TAG_NIKON_TYPE2_FLASH_SYNC_MODE = 0x0008;
    112152
    113153    /**
    114      * Values observed
    115      * - Built-in,TTL
    116      * - Optional,TTL (with speedlight SB800, flash sync mode as NORMAL.  NikonBrowser reports Auto Flash Comp: 0 EV -- which tag is that?) (D70)
    117      * - NEW_TTL (Nikon Browser interprets as "D-TTL")
    118      * - (blank -- accompanied FlashSyncMode of NORMAL) (D70)
     154     * The type of flash used in the photograph, as a string.  Examples include:
     155     *
     156     * <ul>
     157     * <li><code></code></li>
     158     * <li><code>Built-in,TTL</code></li>
     159     * <li><code>NEW_TTL</code> Nikon Browser interprets as "D-TTL"</li>
     160     * <li><code>Built-in,M</code></li>
     161     * <li><code>Optional,TTL</code> with speedlight SB800, flash sync mode as "NORMAL"</li>
     162     * </ul>
    119163     */
    120164    public static final int TAG_NIKON_TYPE2_AUTO_FLASH_MODE = 0x0009;
    121165
    122166    /**
    123      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     167     * An unknown tag, as a rational.  Several values given here:
     168     * http://gvsoft.homedns.org/exif/makernote-nikon-type2.html#0x000b
    124169     */
    125170    public static final int TAG_NIKON_TYPE2_UNKNOWN_34 = 0x000A;
    126171
    127172    /**
    128      * Values observed
    129      * - 0
     173     * The camera's white balance bias setting, as an uint16 array having either one or two elements.
     174     *
     175     * <ul>
     176     * <li><code>0</code></li>
     177     * <li><code>1</code></li>
     178     * <li><code>-3</code></li>
     179     * <li><code>-2</code></li>
     180     * <li><code>-1</code></li>
     181     * <li><code>0,0</code></li>
     182     * <li><code>1,0</code></li>
     183     * <li><code>5,-5</code></li>
     184     * </ul>
    130185     */
    131186    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE = 0x000B;
     
    143198
    144199    /**
    145      * Values observed
    146      * - 0,1,6,0 (hex)
    147      */
    148     public static final int TAG_NIKON_TYPE2_UNKNOWN_1 = 0x000D;
    149 
    150     /**
    151      * Values observed
    152      * - 0,1,c,0 (hex)
    153      */
    154     public static final int TAG_NIKON_TYPE2_UNKNOWN_2 = 0x000E;
     200     * The camera's program shift setting, as an array of four integers.
     201     * The value, in EV, is calculated as <code>a*b/c</code>.
     202     *
     203     * <ul>
     204     * <li><code>0,1,3,0</code> = 0 EV</li>
     205     * <li><code>1,1,3,0</code> = 0.33 EV</li>
     206     * <li><code>-3,1,3,0</code> = -1 EV</li>
     207     * <li><code>1,1,2,0</code> = 0.5 EV</li>
     208     * <li><code>2,1,6,0</code> = 0.33 EV</li>
     209     * </ul>
     210     */
     211    public static final int TAG_NIKON_TYPE2_PROGRAM_SHIFT = 0x000D;
     212
     213    /**
     214     * The exposure difference, as an array of four integers.
     215     * The value, in EV, is calculated as <code>a*b/c</code>.
     216     *
     217     * <ul>
     218     * <li><code>-105,1,12,0</code> = -8.75 EV</li>
     219     * <li><code>-72,1,12,0</code> = -6.00 EV</li>
     220     * <li><code>-11,1,12,0</code> = -0.92 EV</li>
     221     * </ul>
     222     */
     223    public static final int TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE = 0x000E;
     224
     225    /**
     226     * The camera's ISO mode, as an uppercase string.
     227     *
     228     * <ul>
     229     * <li><code>AUTO</code></code></li>
     230     * <li><code>MANUAL</code></li>
     231     * </ul>
     232     */
     233    public static final int TAG_NIKON_TYPE2_ISO_MODE = 0x000F;
    155234
    156235    /**
    157236     * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    158237     */
    159     public static final int TAG_NIKON_TYPE2_ISO_SELECTION = 0x000F;
    160 
    161     /**
    162      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    163      */
    164238    public static final int TAG_NIKON_TYPE2_DATA_DUMP = 0x0010;
    165239
    166240    /**
    167      * Values observed
    168      * - 914
    169      * - 1379 (D70)
    170      * - 2781 (D1X)
    171      * - 6942 (D100)
    172      */
    173     public static final int TAG_NIKON_TYPE2_UNKNOWN_3 = 0x0011;
    174 
    175     /**
    176      * Values observed
    177      * - (no value -- blank)
     241     * Preview to another IFD (?)
     242     * <p/>
     243     * Details here: http://gvsoft.homedns.org/exif/makernote-nikon-2-tag0x0011.html
     244     * // TODO if this is another IFD, decode it
     245     */
     246    public static final int TAG_NIKON_TYPE2_PREVIEW_IFD = 0x0011;
     247
     248    /**
     249     * The flash compensation, as an array of four integers.
     250     * The value, in EV, is calculated as <code>a*b/c</code>.
     251     *
     252     * <ul>
     253     * <li><code>-18,1,6,0</code> = -3 EV</li>
     254     * <li><code>4,1,6,0</code> = 0.67 EV</li>
     255     * <li><code>6,1,6,0</code> = 1 EV</li>
     256     * </ul>
    178257     */
    179258    public static final int TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION = 0x0012;
    180259
    181260    /**
    182      * Values observed
    183      * - 0 250
    184      * - 0 400
    185      */
    186     public static final int TAG_NIKON_TYPE2_ISO_2 = 0x0013;
    187 
    188     /**
    189      * Values observed
     261     * The requested ISO value, as an array of two integers.
     262     *
     263     * <ul>
     264     * <li><code>0,0</code></li>
     265     * <li><code>0,125</code></li>
     266     * <li><code>1,2500</code></li>
     267     * </ul>
     268     */
     269    public static final int TAG_NIKON_TYPE2_ISO_REQUESTED = 0x0013;
     270
     271    /**
     272     * Defines the photo corner coordinates, in 8 bytes.  Treated as four 16-bit integers, they
     273     * decode as: top-left (x,y); bot-right (x,y)
    190274     * - 0 0 49163 53255
    191275     * - 0 0 3008 2000 (the image dimensions were 3008x2000) (D70)
    192      */
    193     public static final int TAG_NIKON_TYPE2_UNKNOWN_21 = 0x0016;
    194 
    195     /**
    196      * Values observed
    197      * - (blank)
    198      */
    199     public static final int TAG_NIKON_TYPE2_UNKNOWN_22 = 0x0017;
    200 
    201     /**
    202      * Values observed
    203      * - (blank)
    204      */
    205     public static final int TAG_NIKON_TYPE2_UNKNOWN_23 = 0x0018;
    206 
    207     /**
    208      * Values observed
    209      * - 0
    210      */
    211     public static final int TAG_NIKON_TYPE2_UNKNOWN_24 = 0x0019;
    212 
    213     /**
    214      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     276     * <ul>
     277     * <li><code>0,0,4288,2848</code> The max resolution of the D300 camera</li>
     278     * <li><code>0,0,3008,2000</code> The max resolution of the D70 camera</li>
     279     * <li><code>0,0,4256,2832</code> The max resolution of the D3 camera</li>
     280     * </ul>
     281     */
     282    public static final int TAG_NIKON_TYPE2_IMAGE_BOUNDARY = 0x0016;
     283
     284    /**
     285     * The flash exposure compensation, as an array of four integers.
     286     * The value, in EV, is calculated as <code>a*b/c</code>.
     287     *
     288     * <ul>
     289     * <li><code>0,0,0,0</code> = 0 EV</li>
     290     * <li><code>0,1,6,0</code> = 0 EV</li>
     291     * <li><code>4,1,6,0</code> = 0.67 EV</li>
     292     * </ul>
     293     */
     294    public static final int TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION = 0x0017;
     295
     296    /**
     297     * The flash bracket compensation, as an array of four integers.
     298     * The value, in EV, is calculated as <code>a*b/c</code>.
     299     *
     300     * <ul>
     301     * <li><code>0,0,0,0</code> = 0 EV</li>
     302     * <li><code>0,1,6,0</code> = 0 EV</li>
     303     * <li><code>4,1,6,0</code> = 0.67 EV</li>
     304     * </ul>
     305     */
     306    public static final int TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION = 0x0018;
     307
     308    /**
     309     * The AE bracket compensation, as a rational number.
     310     *
     311     * <ul>
     312     * <li><code>0/0</code></li>
     313     * <li><code>0/1</code></li>
     314     * <li><code>0/6</code></li>
     315     * <li><code>4/6</code></li>
     316     * <li><code>6/6</code></li>
     317     * </ul>
     318     */
     319    public static final int TAG_NIKON_TYPE2_AE_BRACKET_COMPENSATION = 0x0019;
     320
     321    /**
     322     * Flash mode, as a string.
     323     *
     324     * <ul>
     325     * <li><code></code></li>
     326     * <li><code>Red Eye Reduction</code></li>
     327     * <li><code>D-Lighting</code></li>
     328     * <li><code>Distortion control</code></li>
     329     * </ul>
     330     */
     331    public static final int TAG_NIKON_TYPE2_FLASH_MODE = 0x001a;
     332
     333    public static final int TAG_NIKON_TYPE2_CROP_HIGH_SPEED = 0x001b;
     334    public static final int TAG_NIKON_TYPE2_EXPOSURE_TUNING = 0x001c;
     335
     336    /**
     337     * The camera's serial number, as a string.
     338     * Note that D200 is always blank, and D50 is always <code>"D50"</code>.
     339     */
     340    public static final int TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER = 0x001d;
     341
     342    /**
     343     * The camera's color space setting.
     344     *
     345     * <ul>
     346     * <li><code>1</code> sRGB</li>
     347     * <li><code>2</code> Adobe RGB</li>
     348     * </ul>
     349     */
     350    public static final int TAG_NIKON_TYPE2_COLOR_SPACE = 0x001e;
     351    public static final int TAG_NIKON_TYPE2_VR_INFO = 0x001f;
     352    public static final int TAG_NIKON_TYPE2_IMAGE_AUTHENTICATION = 0x0020;
     353    public static final int TAG_NIKON_TYPE2_UNKNOWN_35 = 0x0021;
     354
     355    /**
     356     * The active D-Lighting setting.
     357     *
     358     * <ul>
     359     * <li><code>0</code> Off</li>
     360     * <li><code>1</code> Low</li>
     361     * <li><code>3</code> Normal</li>
     362     * <li><code>5</code> High</li>
     363     * <li><code>7</code> Extra High</li>
     364     * <li><code>65535</code> Auto</li>
     365     * </ul>
     366     */
     367    public static final int TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING = 0x0022;
     368    public static final int TAG_NIKON_TYPE2_PICTURE_CONTROL = 0x0023;
     369    public static final int TAG_NIKON_TYPE2_WORLD_TIME = 0x0024;
     370    public static final int TAG_NIKON_TYPE2_ISO_INFO = 0x0025;
     371    public static final int TAG_NIKON_TYPE2_UNKNOWN_36 = 0x0026;
     372    public static final int TAG_NIKON_TYPE2_UNKNOWN_37 = 0x0027;
     373    public static final int TAG_NIKON_TYPE2_UNKNOWN_38 = 0x0028;
     374    public static final int TAG_NIKON_TYPE2_UNKNOWN_39 = 0x0029;
     375
     376    /**
     377     * The camera's vignette control setting.
     378     *
     379     * <ul>
     380     * <li><code>0</code> Off</li>
     381     * <li><code>1</code> Low</li>
     382     * <li><code>3</code> Normal</li>
     383     * <li><code>5</code> High</li>
     384     * </ul>
     385     */
     386    public static final int TAG_NIKON_TYPE2_VIGNETTE_CONTROL = 0x002a;
     387    public static final int TAG_NIKON_TYPE2_UNKNOWN_40 = 0x002b;
     388    public static final int TAG_NIKON_TYPE2_UNKNOWN_41 = 0x002c;
     389    public static final int TAG_NIKON_TYPE2_UNKNOWN_42 = 0x002d;
     390    public static final int TAG_NIKON_TYPE2_UNKNOWN_43 = 0x002e;
     391    public static final int TAG_NIKON_TYPE2_UNKNOWN_44 = 0x002f;
     392    public static final int TAG_NIKON_TYPE2_UNKNOWN_45 = 0x0030;
     393    public static final int TAG_NIKON_TYPE2_UNKNOWN_46 = 0x0031;
     394
     395    /**
     396     * The camera's image adjustment setting, as a string.
     397     *
     398     * <ul>
     399     * <li><code>AUTO</code></li>
     400     * <li><code>CONTRAST(+)</code></li>
     401     * <li><code>CONTRAST(-)</code></li>
     402     * <li><code>NORMAL</code></li>
     403     * <li><code>B & W</code></li>
     404     * <li><code>BRIGHTNESS(+)</code></li>
     405     * <li><code>BRIGHTNESS(-)</code></li>
     406     * <li><code>SEPIA</code></li>
     407     * </ul>
    215408     */
    216409    public static final int TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT = 0x0080;
    217410
    218411    /**
    219      * The tone compensation as set in the camera.
    220      *
    221      * Values observed
    222      * - AUTO
    223      * - NORMAL (D1X, D100)
     412     * The camera's tone compensation setting, as a string.
     413     *
     414     * <ul>
     415     * <li><code>NORMAL</code></li>
     416     * <li><code>LOW</code></li>
     417     * <li><code>MED.L</code></li>
     418     * <li><code>MED.H</code></li>
     419     * <li><code>HIGH</code></li>
     420     * <li><code>AUTO</code></li>
     421     * </ul>
    224422     */
    225423    public static final int TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION = 0x0081;
    226424
    227425    /**
    228      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     426     * A description of any auxiliary lens, as a string.
     427     *
     428     * <ul>
     429     * <li><code>OFF</code></li>
     430     * <li><code>FISHEYE 1</code></li>
     431     * <li><code>FISHEYE 2</code></li>
     432     * <li><code>TELEPHOTO 2</code></li>
     433     * <li><code>WIDE ADAPTER</code></li>
     434     * </ul>
    229435     */
    230436    public static final int TAG_NIKON_TYPE2_ADAPTER = 0x0082;
    231437
    232438    /**
    233      * Values observed
    234      * - 6
    235      * - 6 (D70)
    236      * - 2 (D1X)
    237      */
    238     public static final int TAG_NIKON_TYPE2_UNKNOWN_4 = 0x0083;
     439     * The type of lens used, as a byte.
     440     *
     441     * <ul>
     442     * <li><code>0x00</code> AF</li>
     443     * <li><code>0x01</code> MF</li>
     444     * <li><code>0x02</code> D</li>
     445     * <li><code>0x06</code> G, D</li>
     446     * <li><code>0x08</code> VR</li>
     447     * <li><code>0x0a</code> VR, D</li>
     448     * <li><code>0x0e</code> VR, G, D</li>
     449     * </ul>
     450     */
     451    public static final int TAG_NIKON_TYPE2_LENS_TYPE = 0x0083;
    239452
    240453    /**
     
    260473
    261474    /**
    262      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     475     * The amount of digital zoom used.
    263476     */
    264477    public static final int TAG_NIKON_TYPE2_DIGITAL_ZOOM = 0x0086;
    265478
    266479    /**
     480     * Whether the flash was used in this image.
     481     *
     482     * <ul>
     483     * <li><code>0</code> Flash Not Used</li>
     484     * <li><code>1</code> Manual Flash</li>
     485     * <li><code>3</code> Flash Not Ready</li>
     486     * <li><code>7</code> External Flash</li>
     487     * <li><code>8</code> Fired, Commander Mode</li>
     488     * <li><code>9</code> Fired, TTL Mode</li>
     489     * </ul>
     490     */
     491    public static final int TAG_NIKON_TYPE2_FLASH_USED = 0x0087;
     492
     493    /**
     494     * The position of the autofocus target.
     495     */
     496    public static final int TAG_NIKON_TYPE2_AF_FOCUS_POSITION = 0x0088;
     497
     498    /**
     499     * The camera's shooting mode.
     500     * <p/>
     501     * A bit-array with:
     502     * <ul>
     503     * <li><code>0</code> Single Frame</li>
     504     * <li><code>1</code> Continuous</li>
     505     * <li><code>2</code> Delay</li>
     506     * <li><code>8</code> PC Control</li>
     507     * <li><code>16</code> Exposure Bracketing</li>
     508     * <li><code>32</code> Auto ISO</li>
     509     * <li><code>64</code> White-Balance Bracketing</li>
     510     * <li><code>128</code> IR Control</li>
     511     * </ul>
     512     */
     513    public static final int TAG_NIKON_TYPE2_SHOOTING_MODE = 0x0089;
     514
     515    public static final int TAG_NIKON_TYPE2_UNKNOWN_20 = 0x008A;
     516
     517    /**
     518     * Lens stops, as an array of four integers.
     519     * The value, in EV, is calculated as <code>a*b/c</code>.
     520     *
     521     * <ul>
     522     * <li><code>64,1,12,0</code> = 5.33 EV</li>
     523     * <li><code>72,1,12,0</code> = 6 EV</li>
     524     * </ul>
     525     */
     526    public static final int TAG_NIKON_TYPE2_LENS_STOPS = 0x008B;
     527
     528    public static final int TAG_NIKON_TYPE2_CONTRAST_CURVE = 0x008C;
     529
     530    /**
     531     * The color space as set in the camera, as a string.
     532     *
     533     * <ul>
     534     * <li><code>MODE1</code> = Mode 1 (sRGB)</li>
     535     * <li><code>MODE1a</code> = Mode 1 (sRGB)</li>
     536     * <li><code>MODE2</code> = Mode 2 (Adobe RGB)</li>
     537     * <li><code>MODE3</code> = Mode 2 (sRGB): Higher Saturation</li>
     538     * <li><code>MODE3a</code> = Mode 2 (sRGB): Higher Saturation</li>
     539     * <li><code>B & W</code> = B & W</li>
     540     * </ul>
     541     */
     542    public static final int TAG_NIKON_TYPE2_CAMERA_COLOR_MODE = 0x008D;
     543    public static final int TAG_NIKON_TYPE2_UNKNOWN_47 = 0x008E;
     544
     545    /**
     546     * The camera's scene mode, as a string.  Examples include:
     547     * <ul>
     548     * <li><code>BEACH/SNOW</code></li>
     549     * <li><code>CLOSE UP</code></li>
     550     * <li><code>NIGHT PORTRAIT</code></li>
     551     * <li><code>PORTRAIT</code></li>
     552     * <li><code>ANTI-SHAKE</code></li>
     553     * <li><code>BACK LIGHT</code></li>
     554     * <li><code>BEST FACE</code></li>
     555     * <li><code>BEST</code></li>
     556     * <li><code>COPY</code></li>
     557     * <li><code>DAWN/DUSK</code></li>
     558     * <li><code>FACE-PRIORITY</code></li>
     559     * <li><code>FIREWORKS</code></li>
     560     * <li><code>FOOD</code></li>
     561     * <li><code>HIGH SENS.</code></li>
     562     * <li><code>LAND SCAPE</code></li>
     563     * <li><code>MUSEUM</code></li>
     564     * <li><code>PANORAMA ASSIST</code></li>
     565     * <li><code>PARTY/INDOOR</code></li>
     566     * <li><code>SCENE AUTO</code></li>
     567     * <li><code>SMILE</code></li>
     568     * <li><code>SPORT</code></li>
     569     * <li><code>SPORT CONT.</code></li>
     570     * <li><code>SUNSET</code></li>
     571     * </ul>
     572     */
     573    public static final int TAG_NIKON_TYPE2_SCENE_MODE = 0x008F;
     574
     575    /**
     576     * The lighting type, as a string.  Examples include:
     577     * <ul>
     578     * <li><code></code></li>
     579     * <li><code>NATURAL</code></li>
     580     * <li><code>SPEEDLIGHT</code></li>
     581     * <li><code>COLORED</code></li>
     582     * <li><code>MIXED</code></li>
     583     * <li><code>NORMAL</code></li>
     584     * </ul>
     585     */
     586    public static final int TAG_NIKON_TYPE2_LIGHT_SOURCE = 0x0090;
     587
     588    /**
     589     * Advertised as ASCII, but actually isn't.  A variable number of bytes (eg. 18 to 533).  Actual number of bytes
     590     * appears fixed for a given camera model.
     591     */
     592    public static final int TAG_NIKON_TYPE2_SHOT_INFO = 0x0091;
     593
     594    /**
     595     * The hue adjustment as set in the camera.  Values observed are either 0 or 3.
     596     */
     597    public static final int TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT = 0x0092;
     598    /**
     599     * The NEF (RAW) compression.  Examples include:
     600     * <ul>
     601     * <li><code>1</code> Lossy (Type 1)</li>
     602     * <li><code>2</code> Uncompressed</li>
     603     * <li><code>3</code> Lossless</li>
     604     * <li><code>4</code> Lossy (Type 2)</li>
     605     * </ul>
     606     */
     607    public static final int TAG_NIKON_TYPE2_NEF_COMPRESSION = 0x0093;
     608   
     609    /**
     610     * The saturation level, as a signed integer.  Examples include:
     611     * <ul>
     612     * <li><code>+3</code></li>
     613     * <li><code>+2</code></li>
     614     * <li><code>+1</code></li>
     615     * <li><code>0</code> Normal</li>
     616     * <li><code>-1</code></li>
     617     * <li><code>-2</code></li>
     618     * <li><code>-3</code> (B&W)</li>
     619     * </ul>
     620     */
     621    public static final int TAG_NIKON_TYPE2_SATURATION = 0x0094;
     622
     623    /**
     624     * The type of noise reduction, as a string.  Examples include:
     625     * <ul>
     626     * <li><code>OFF</code></li>
     627     * <li><code>FPNR</code></li>
     628     * </ul>
     629     */
     630    public static final int TAG_NIKON_TYPE2_NOISE_REDUCTION = 0x0095;
     631    public static final int TAG_NIKON_TYPE2_LINEARIZATION_TABLE = 0x0096;
     632    public static final int TAG_NIKON_TYPE2_COLOR_BALANCE = 0x0097;
     633    public static final int TAG_NIKON_TYPE2_LENS_DATA = 0x0098;
     634
     635    /** The NEF (RAW) thumbnail size, as an integer array with two items representing [width,height]. */
     636    public static final int TAG_NIKON_TYPE2_NEF_THUMBNAIL_SIZE = 0x0099;
     637
     638    /** The sensor pixel size, as a pair of rational numbers. */
     639    public static final int TAG_NIKON_TYPE2_SENSOR_PIXEL_SIZE = 0x009A;
     640    public static final int TAG_NIKON_TYPE2_UNKNOWN_10 = 0x009B;
     641    public static final int TAG_NIKON_TYPE2_SCENE_ASSIST = 0x009C;
     642    public static final int TAG_NIKON_TYPE2_UNKNOWN_11 = 0x009D;
     643    public static final int TAG_NIKON_TYPE2_RETOUCH_HISTORY = 0x009E;
     644    public static final int TAG_NIKON_TYPE2_UNKNOWN_12 = 0x009F;
     645
     646    /**
     647     * The camera serial number, as a string.
     648     * <ul>
     649     * <li><code>NO= 00002539</code></li>
     650     * <li><code>NO= -1000d71</code></li>
     651     * <li><code>PKG597230621263</code></li>
     652     * <li><code>PKG5995671330625116</code></li>
     653     * <li><code>PKG49981281631130677</code></li>
     654     * <li><code>BU672230725063</code></li>
     655     * <li><code>NO= 200332c7</code></li>
     656     * <li><code>NO= 30045efe</code></li>
     657     * </ul>
     658     */
     659    public static final int TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER_2 = 0x00A0;
     660
     661    public static final int TAG_NIKON_TYPE2_IMAGE_DATA_SIZE = 0x00A2;
     662
     663    public static final int TAG_NIKON_TYPE2_UNKNOWN_27 = 0x00A3;
     664    public static final int TAG_NIKON_TYPE2_UNKNOWN_28 = 0x00A4;
     665    public static final int TAG_NIKON_TYPE2_IMAGE_COUNT = 0x00A5;
     666    public static final int TAG_NIKON_TYPE2_DELETED_IMAGE_COUNT = 0x00A6;
     667
     668    /** The number of total shutter releases.  This value increments for each exposure (observed on D70). */
     669    public static final int TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER = 0x00A7;
     670
     671    public static final int TAG_NIKON_TYPE2_FLASH_INFO = 0x00A8;
     672    /**
     673     * The camera's image optimisation, as a string.
     674     * <ul>
     675     *     <li><code></code></li>
     676     *     <li><code>NORMAL</code></li>
     677     *     <li><code>CUSTOM</code></li>
     678     *     <li><code>BLACK AND WHITE</code></li>
     679     *     <li><code>LAND SCAPE</code></li>
     680     *     <li><code>MORE VIVID</code></li>
     681     *     <li><code>PORTRAIT</code></li>
     682     *     <li><code>SOFT</code></li>
     683     *     <li><code>VIVID</code></li>
     684     * </ul>
     685     */
     686    public static final int TAG_NIKON_TYPE2_IMAGE_OPTIMISATION = 0x00A9;
     687
     688    /**
     689     * The camera's saturation level, as a string.
     690     * <ul>
     691     *     <li><code></code></li>
     692     *     <li><code>NORMAL</code></li>
     693     *     <li><code>AUTO</code></li>
     694     *     <li><code>ENHANCED</code></li>
     695     *     <li><code>MODERATE</code></li>
     696     * </ul>
     697     */
     698    public static final int TAG_NIKON_TYPE2_SATURATION_2 = 0x00AA;
     699
     700    /**
     701     * The camera's digital vari-program setting, as a string.
     702     * <ul>
     703     *     <li><code></code></li>
     704     *     <li><code>AUTO</code></li>
     705     *     <li><code>AUTO(FLASH OFF)</code></li>
     706     *     <li><code>CLOSE UP</code></li>
     707     *     <li><code>LANDSCAPE</code></li>
     708     *     <li><code>NIGHT PORTRAIT</code></li>
     709     *     <li><code>PORTRAIT</code></li>
     710     *     <li><code>SPORT</code></li>
     711     * </ul>
     712     */
     713    public static final int TAG_NIKON_TYPE2_DIGITAL_VARI_PROGRAM = 0x00AB;
     714
     715    /**
     716     * The camera's digital vari-program setting, as a string.
     717     * <ul>
     718     *     <li><code></code></li>
     719     *     <li><code>VR-ON</code></li>
     720     *     <li><code>VR-OFF</code></li>
     721     *     <li><code>VR-HYBRID</code></li>
     722     *     <li><code>VR-ACTIVE</code></li>
     723     * </ul>
     724     */
     725    public static final int TAG_NIKON_TYPE2_IMAGE_STABILISATION = 0x00AC;
     726
     727    /**
     728     * The camera's digital vari-program setting, as a string.
     729     * <ul>
     730     *     <li><code></code></li>
     731     *     <li><code>HYBRID</code></li>
     732     *     <li><code>STANDARD</code></li>
     733     * </ul>
     734     */
     735    public static final int TAG_NIKON_TYPE2_AF_RESPONSE = 0x00AD;
     736    public static final int TAG_NIKON_TYPE2_UNKNOWN_29 = 0x00AE;
     737    public static final int TAG_NIKON_TYPE2_UNKNOWN_30 = 0x00AF;
     738    public static final int TAG_NIKON_TYPE2_MULTI_EXPOSURE = 0x00B0;
     739
     740    /**
     741     * The camera's high ISO noise reduction setting, as an integer.
     742     * <ul>
     743     *     <li><code>0</code> Off</li>
     744     *     <li><code>1</code> Minimal</li>
     745     *     <li><code>2</code> Low</li>
     746     *     <li><code>4</code> Normal</li>
     747     *     <li><code>6</code> High</li>
     748     * </ul>
     749     */
     750    public static final int TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION = 0x00B1;
     751    public static final int TAG_NIKON_TYPE2_UNKNOWN_31 = 0x00B2;
     752    public static final int TAG_NIKON_TYPE2_UNKNOWN_32 = 0x00B3;
     753    public static final int TAG_NIKON_TYPE2_UNKNOWN_33 = 0x00B4;
     754    public static final int TAG_NIKON_TYPE2_UNKNOWN_48 = 0x00B5;
     755    public static final int TAG_NIKON_TYPE2_POWER_UP_TIME = 0x00B6;
     756    public static final int TAG_NIKON_TYPE2_AF_INFO_2 = 0x00B7;
     757    public static final int TAG_NIKON_TYPE2_FILE_INFO = 0x00B8;
     758    public static final int TAG_NIKON_TYPE2_AF_TUNE = 0x00B9;
     759    public static final int TAG_NIKON_TYPE2_UNKNOWN_49 = 0x00BB;
     760    public static final int TAG_NIKON_TYPE2_UNKNOWN_50 = 0x00BD;
     761    public static final int TAG_NIKON_TYPE2_UNKNOWN_51 = 0x0103;
     762    public static final int TAG_NIKON_TYPE2_PRINT_IM = 0x0E00;
     763
     764    /**
     765     * Data about changes set by Nikon Capture Editor.
     766     *
    267767     * Values observed
    268      * - 0
    269      * - 9
    270      * - 3 (D1X)
    271      */
    272     public static final int TAG_NIKON_TYPE2_UNKNOWN_5 = 0x0087;
    273 
    274     /**
    275      * Values observed
    276      * -
    277      */
    278     public static final int TAG_NIKON_TYPE2_AF_FOCUS_POSITION = 0x0088;
    279 
    280     /**
    281      * Values observed
    282      * - 0
    283      * - 1
    284      */
    285     public static final int TAG_NIKON_TYPE2_UNKNOWN_7 = 0x0089;
    286 
    287     /**
    288      * Values observed
    289      * - 0
    290      * - 0
    291      */
    292     public static final int TAG_NIKON_TYPE2_UNKNOWN_20 = 0x008A;
    293 
    294     /**
    295      * Values observed
    296      * - 48,1,c,0 (hex) (D100)
    297      * - @ <hex>
    298      */
    299     public static final int TAG_NIKON_TYPE2_UNKNOWN_8 = 0x008B;
    300 
    301     /**
    302      * Unknown.  Fabrizio believes this may be a lookup table for the user-defined curve.
    303      *
    304      * Values observed
    305      * - (blank) (D1X)
    306      */
    307     public static final int TAG_NIKON_TYPE2_UNKNOWN_9 = 0x008C;
    308 
    309     /**
    310      * The color space as set in the camera.
    311      *
    312      * Values observed
    313      * - MODE1
    314      * - Mode I (sRGB) (D70)
    315      * - MODE2 (D1X, D100)
    316      */
    317     public static final int TAG_NIKON_TYPE2_CAMERA_COLOR_MODE = 0x008D;
    318 
    319     /**
    320      * Values observed
    321      * - NATURAL
    322      * - SPEEDLIGHT (D70, D1X)
    323      */
    324     public static final int TAG_NIKON_TYPE2_LIGHT_SOURCE = 0x0090;
    325 
    326     /**
    327      * Values observed
    328      * - 0100 <hex>
    329      * - 0103 (D70)
    330      * - 0100 (D1X)
    331      */
    332     public static final int TAG_NIKON_TYPE2_UNKNOWN_11 = 0x0091;
    333 
    334     /**
    335      * The hue adjustment as set in the camera.
    336      *
    337      * Values observed
    338      * - 0
    339      */
    340     public static final int TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT = 0x0092;
    341 
    342     /**
    343      * Values observed
    344      * - OFF
    345      */
    346     public static final int TAG_NIKON_TYPE2_NOISE_REDUCTION = 0x0095;
    347 
    348     /**
    349      * Values observed
    350      * - 0100 <hex>
    351      * - 0103 <hex>
    352      */
    353     public static final int TAG_NIKON_TYPE2_UNKNOWN_12 = 0x0097;
    354 
    355     /**
    356      * Values observed
    357      * - 0100 <hex>
    358      * - 0101 <hex>
    359      * - 0100 <hex> (D1X)
    360      * - 30,31,30,30,0,0,b,48,7c,7c,24,24,5,15,24,0,0,0,0,0 (hex) (D100)
    361      */
    362     public static final int TAG_NIKON_TYPE2_UNKNOWN_13 = 0x0098;
    363 
    364     /**
    365      * Values observed
    366      * - 2014 662 (D1X)
    367      * - 1517,1012 (D100)
    368      */
    369     public static final int TAG_NIKON_TYPE2_UNKNOWN_14 = 0x0099;
    370 
    371     /**
    372      * Values observed
    373      * - 78/10 78/10
    374      * - 78/10 78/10 (D70)
    375      * - 59/10 59/5 (D1X)
    376      * - 7.8,7.8 (D100)
    377      */
    378     public static final int TAG_NIKON_TYPE2_UNKNOWN_15 = 0x009A;
    379 
    380     /**
    381      * Values observed
    382      * - NO= 00002539
    383      */
    384     public static final int TAG_NIKON_TYPE2_UNKNOWN_25 = 0x00A0;
    385 
    386     /**
    387      * Values observed
    388      * - 1564851
    389      */
    390     public static final int TAG_NIKON_TYPE2_UNKNOWN_26 = 0x00A2;
    391 
    392     /**
    393      * Values observed
    394      * - 0
    395      */
    396     public static final int TAG_NIKON_TYPE2_UNKNOWN_27 = 0x00A3;
    397 
    398     /**
    399      * This appears to be a sequence number to indentify the exposure.  This value seems to increment
    400      * for consecutive exposures (observed on D70).
    401      *
    402      * Values observed
    403      * - 5062
    404      */
    405     public static final int TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER = 0x00A7;
    406 
    407     /**
    408      * Values observed
    409      * - 0100 (D70)
    410      */
    411     public static final int TAG_NIKON_TYPE2_UNKNOWN_32 = 0x00A8;
    412 
    413     /**
    414      * Values observed
    415      * - NORMAL (D70)
    416      */
    417     public static final int TAG_NIKON_TYPE2_UNKNOWN_33 = 0x00A9;
    418 
    419     /**
    420      * Nikon Browser suggests this value represents Saturation...
    421      * Values observed
    422      * - NORMAL (D70)
    423      */
    424     public static final int TAG_NIKON_TYPE2_UNKNOWN_29 = 0x00AA;
    425 
    426     /**
    427      * Values observed
    428      * - AUTO (D70)
    429      * - (blank) (D70)
    430      */
    431     public static final int TAG_NIKON_TYPE2_UNKNOWN_30 = 0x00AB;
    432 
    433     /**
    434      * Data about changes set by Nikon Capture Editor.
    435      *
    436      * Values observed
    437      */
    438     public static final int TAG_NIKON_TYPE2_CAPTURE_EDITOR_DATA = 0x0E01;
    439 
    440     /**
    441      * Values observed
    442      * - 1473
    443      * - 7036 (D100)
    444      */
    445     public static final int TAG_NIKON_TYPE2_UNKNOWN_16 = 0x0E10;
    446 
    447     protected static final HashMap _tagNameMap = new HashMap();
     768     */
     769    public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_DATA = 0x0E01;
     770    public static final int TAG_NIKON_TYPE2_UNKNOWN_52 = 0x0E05;
     771    public static final int TAG_NIKON_TYPE2_UNKNOWN_53 = 0x0E08;
     772    public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_VERSION = 0x0E09;
     773    public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_OFFSETS = 0x0E0E;
     774    public static final int TAG_NIKON_TYPE2_NIKON_SCAN = 0x0E10;
     775    public static final int TAG_NIKON_TYPE2_UNKNOWN_54 = 0x0E19;
     776    public static final int TAG_NIKON_TYPE2_NEF_BIT_DEPTH = 0x0E22;
     777    public static final int TAG_NIKON_TYPE2_UNKNOWN_55 = 0x0E23;
     778
     779    @NotNull
     780    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    448781
    449782    static
    450783    {
    451         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_FIRMWARE_VERSION), "Firmware Version");
    452         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_1), "ISO");
    453         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT), "Quality & File Format");
    454         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE), "White Balance");
    455         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_SHARPENING), "Sharpening");
    456         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AF_TYPE), "AF Type");
    457         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE), "White Balance Fine");
    458         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF), "White Balance RB Coefficients");
    459         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_2), "ISO");
    460         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_SELECTION), "ISO Selection");
    461         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_DATA_DUMP), "Data Dump");
    462         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT), "Image Adjustment");
    463         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION), "Tone Compensation");
    464         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ADAPTER), "Adapter");
    465         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_LENS), "Lens");
    466         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE), "Manual Focus Distance");
    467         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_DIGITAL_ZOOM), "Digital Zoom");
    468         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_COLOR_MODE), "Colour Mode");
    469         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT), "Camera Hue Adjustment");
    470         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_NOISE_REDUCTION), "Noise Reduction");
    471         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAPTURE_EDITOR_DATA), "Capture Editor Data");
    472 
    473         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_1), "Unknown 01");
    474         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_2), "Unknown 02");
    475         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_3), "Unknown 03");
    476         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_4), "Unknown 04");
    477         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_5), "Unknown 05");
    478         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AF_FOCUS_POSITION), "AF Focus Position");
    479         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_7), "Unknown 07");
    480         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_8), "Unknown 08");
    481         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_9), "Unknown 09");
    482         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_LIGHT_SOURCE), "Light source");
    483         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_11), "Unknown 11");
    484         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_12), "Unknown 12");
    485         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_13), "Unknown 13");
    486         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_14), "Unknown 14");
    487         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_15), "Unknown 15");
    488         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_16), "Unknown 16");
    489         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_FLASH_SYNC_MODE), "Flash Sync Mode");
    490         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AUTO_FLASH_MODE), "Auto Flash Mode");
    491         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION), "Auto Flash Compensation");
    492         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER), "Exposure Sequence Number");
    493         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_COLOR_MODE), "Color Mode");
    494 
    495         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_20), "Unknown 20");
    496         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_21), "Unknown 21");
    497         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_22), "Unknown 22");
    498         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_23), "Unknown 23");
    499         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_24), "Unknown 24");
    500         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_25), "Unknown 25");
    501         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_26), "Unknown 26");
    502         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_27), "Unknown 27");
    503         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_29), "Unknown 29");
    504         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_30), "Unknown 30");
    505         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_32), "Unknown 32");
    506         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_33), "Unknown 33");
     784        _tagNameMap.put(TAG_NIKON_TYPE2_FIRMWARE_VERSION, "Firmware Version");
     785        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_1, "ISO");
     786        _tagNameMap.put(TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT, "Quality & File Format");
     787        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE, "White Balance");
     788        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SHARPENING, "Sharpening");
     789        _tagNameMap.put(TAG_NIKON_TYPE2_AF_TYPE, "AF Type");
     790        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE, "White Balance Fine");
     791        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF, "White Balance RB Coefficients");
     792        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_REQUESTED, "ISO");
     793        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_MODE, "ISO Mode");
     794        _tagNameMap.put(TAG_NIKON_TYPE2_DATA_DUMP, "Data Dump");
     795
     796        _tagNameMap.put(TAG_NIKON_TYPE2_PROGRAM_SHIFT, "Program Shift");
     797        _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE, "Exposure Difference");
     798        _tagNameMap.put(TAG_NIKON_TYPE2_PREVIEW_IFD, "Preview IFD");
     799        _tagNameMap.put(TAG_NIKON_TYPE2_LENS_TYPE, "Lens Type");
     800        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_USED, "Flash Used");
     801        _tagNameMap.put(TAG_NIKON_TYPE2_AF_FOCUS_POSITION, "AF Focus Position");
     802        _tagNameMap.put(TAG_NIKON_TYPE2_SHOOTING_MODE, "Shooting Mode");
     803        _tagNameMap.put(TAG_NIKON_TYPE2_LENS_STOPS, "Lens Stops");
     804        _tagNameMap.put(TAG_NIKON_TYPE2_CONTRAST_CURVE, "Contrast Curve");
     805        _tagNameMap.put(TAG_NIKON_TYPE2_LIGHT_SOURCE, "Light source");
     806        _tagNameMap.put(TAG_NIKON_TYPE2_SHOT_INFO, "Shot Info");
     807        _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_BALANCE, "Color Balance");
     808        _tagNameMap.put(TAG_NIKON_TYPE2_LENS_DATA, "Lens Data");
     809        _tagNameMap.put(TAG_NIKON_TYPE2_NEF_THUMBNAIL_SIZE, "NEF Thumbnail Size");
     810        _tagNameMap.put(TAG_NIKON_TYPE2_SENSOR_PIXEL_SIZE, "Sensor Pixel Size");
     811        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_10, "Unknown 10");
     812        _tagNameMap.put(TAG_NIKON_TYPE2_SCENE_ASSIST, "Scene Assist");
     813        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_11, "Unknown 11");
     814        _tagNameMap.put(TAG_NIKON_TYPE2_RETOUCH_HISTORY, "Retouch History");
     815        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_12, "Unknown 12");
     816        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_SYNC_MODE, "Flash Sync Mode");
     817        _tagNameMap.put(TAG_NIKON_TYPE2_AUTO_FLASH_MODE, "Auto Flash Mode");
     818        _tagNameMap.put(TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION, "Auto Flash Compensation");
     819        _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER, "Exposure Sequence Number");
     820        _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_MODE, "Color Mode");
     821
     822        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_20, "Unknown 20");
     823        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_BOUNDARY, "Image Boundary");
     824        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION, "Flash Exposure Compensation");
     825        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION, "Flash Bracket Compensation");
     826        _tagNameMap.put(TAG_NIKON_TYPE2_AE_BRACKET_COMPENSATION, "AE Bracket Compensation");
     827        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_MODE, "Flash Mode");
     828        _tagNameMap.put(TAG_NIKON_TYPE2_CROP_HIGH_SPEED, "Crop High Speed");
     829        _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_TUNING, "Exposure Tuning");
     830        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER, "Camera Serial Number");
     831        _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_SPACE, "Color Space");
     832        _tagNameMap.put(TAG_NIKON_TYPE2_VR_INFO, "VR Info");
     833        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_AUTHENTICATION, "Image Authentication");
     834        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_35, "Unknown 35");
     835        _tagNameMap.put(TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING, "Active D-Lighting");
     836        _tagNameMap.put(TAG_NIKON_TYPE2_PICTURE_CONTROL, "Picture Control");
     837        _tagNameMap.put(TAG_NIKON_TYPE2_WORLD_TIME, "World Time");
     838        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_INFO, "ISO Info");
     839        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_36, "Unknown 36");
     840        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_37, "Unknown 37");
     841        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_38, "Unknown 38");
     842        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_39, "Unknown 39");
     843        _tagNameMap.put(TAG_NIKON_TYPE2_VIGNETTE_CONTROL, "Vignette Control");
     844        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_40, "Unknown 40");
     845        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_41, "Unknown 41");
     846        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_42, "Unknown 42");
     847        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_43, "Unknown 43");
     848        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_44, "Unknown 44");
     849        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_45, "Unknown 45");
     850        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_46, "Unknown 46");
     851        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_47, "Unknown 47");
     852        _tagNameMap.put(TAG_NIKON_TYPE2_SCENE_MODE, "Scene Mode");
     853
     854        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER_2, "Camera Serial Number");
     855        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_DATA_SIZE, "Image Data Size");
     856        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_27, "Unknown 27");
     857        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_28, "Unknown 28");
     858        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_COUNT, "Image Count");
     859        _tagNameMap.put(TAG_NIKON_TYPE2_DELETED_IMAGE_COUNT, "Deleted Image Count");
     860        _tagNameMap.put(TAG_NIKON_TYPE2_SATURATION_2, "Saturation");
     861        _tagNameMap.put(TAG_NIKON_TYPE2_DIGITAL_VARI_PROGRAM, "Digital Vari Program");
     862        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_STABILISATION, "Image Stabilisation");
     863        _tagNameMap.put(TAG_NIKON_TYPE2_AF_RESPONSE, "AF Response");
     864        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_29, "Unknown 29");
     865        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_30, "Unknown 30");
     866        _tagNameMap.put(TAG_NIKON_TYPE2_MULTI_EXPOSURE, "Multi Exposure");
     867        _tagNameMap.put(TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION, "High ISO Noise Reduction");
     868        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_31, "Unknown 31");
     869        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_32, "Unknown 32");
     870        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_33, "Unknown 33");
     871        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_48, "Unknown 48");
     872        _tagNameMap.put(TAG_NIKON_TYPE2_POWER_UP_TIME, "Power Up Time");
     873        _tagNameMap.put(TAG_NIKON_TYPE2_AF_INFO_2, "AF Info 2");
     874        _tagNameMap.put(TAG_NIKON_TYPE2_FILE_INFO, "File Info");
     875        _tagNameMap.put(TAG_NIKON_TYPE2_AF_TUNE, "AF Tune");
     876        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_INFO, "Flash Info");
     877        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_OPTIMISATION, "Image Optimisation");
     878
     879        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT, "Image Adjustment");
     880        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION, "Tone Compensation");
     881        _tagNameMap.put(TAG_NIKON_TYPE2_ADAPTER, "Adapter");
     882        _tagNameMap.put(TAG_NIKON_TYPE2_LENS, "Lens");
     883        _tagNameMap.put(TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE, "Manual Focus Distance");
     884        _tagNameMap.put(TAG_NIKON_TYPE2_DIGITAL_ZOOM, "Digital Zoom");
     885        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_COLOR_MODE, "Colour Mode");
     886        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT, "Camera Hue Adjustment");
     887        _tagNameMap.put(TAG_NIKON_TYPE2_NEF_COMPRESSION, "NEF Compression");
     888        _tagNameMap.put(TAG_NIKON_TYPE2_SATURATION, "Saturation");
     889        _tagNameMap.put(TAG_NIKON_TYPE2_NOISE_REDUCTION, "Noise Reduction");
     890        _tagNameMap.put(TAG_NIKON_TYPE2_LINEARIZATION_TABLE, "Linearization Table");
     891        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_DATA, "Nikon Capture Data");
     892        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_49, "Unknown 49");
     893        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_50, "Unknown 50");
     894        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_51, "Unknown 51");
     895        _tagNameMap.put(TAG_NIKON_TYPE2_PRINT_IM, "Print IM");
     896        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_52, "Unknown 52");
     897        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_53, "Unknown 53");
     898        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_VERSION, "Nikon Capture Version");
     899        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_OFFSETS, "Nikon Capture Offsets");
     900        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_SCAN, "Nikon Scan");
     901        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_54, "Unknown 54");
     902        _tagNameMap.put(TAG_NIKON_TYPE2_NEF_BIT_DEPTH, "NEF Bit Depth");
     903        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_55, "Unknown 55");
    507904    }
    508905
     
    512909    }
    513910
    514     public Rational getAutoFlashCompensation() throws MetadataException
    515     {
    516         if (!containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION))
    517             return null;
    518 
    519         byte[] bytes = getByteArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION);
    520         return CalculateFlashCompensationFromBytes(bytes);
    521     }
    522 
    523     public static Rational CalculateFlashCompensationFromBytes(byte[] bytes)
    524     {
    525         if (bytes.length==3)
    526         {
    527             byte denominator = bytes[2];
    528             int numerator = (int)bytes[0] * bytes[1];
    529             return new Rational(numerator, denominator);
    530         }
    531         return null;
    532     }
    533 
     911    @NotNull
    534912    public String getName()
    535913    {
     
    537915    }
    538916
    539     protected HashMap getTagNameMap()
     917    @NotNull
     918    protected HashMap<Integer, String> getTagNameMap()
    540919    {
    541920        return _tagNameMap;
  • trunk/src/com/drew/metadata/exif/OlympusMakernoteDescriptor.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
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
    2127/**
    22  * Provides human-readable string versions of the tags stored in an OlympusMakernoteDirectory.
     28 * Provides human-readable string representations of tag values stored in a <code>OlympusMakernoteDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
    2331 */
    24 public class OlympusMakernoteDescriptor extends TagDescriptor
     32public class OlympusMakernoteDescriptor extends TagDescriptor<OlympusMakernoteDirectory>
    2533{
    26     public OlympusMakernoteDescriptor(Directory directory)
     34    public OlympusMakernoteDescriptor(@NotNull OlympusMakernoteDirectory directory)
    2735    {
    2836        super(directory);
    2937    }
    3038
    31     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3241    {
    3342        switch (tagType) {
     
    4150                return getDigiZoomRatioDescription();
    4251            default:
    43                 return _directory.getString(tagType);
     52                return super.getDescription(tagType);
    4453        }
    4554    }
    4655
    47     public String getDigiZoomRatioDescription() throws MetadataException
     56    @Nullable
     57    public String getDigiZoomRatioDescription()
    4858    {
    49         if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO)) return null;
    50         int value = _directory.getInt(OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO);
     59        Integer value = _directory.getInteger(OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO);
     60        if (value==null)
     61            return null;
    5162        switch (value) {
    5263            case 0:
     
    5970    }
    6071
    61     public String getMacroModeDescription() throws MetadataException
     72    @Nullable
     73    public String getMacroModeDescription()
    6274    {
    63         if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_MACRO_MODE)) return null;
    64         int value = _directory.getInt(OlympusMakernoteDirectory.TAG_OLYMPUS_MACRO_MODE);
     75        Integer value = _directory.getInteger(OlympusMakernoteDirectory.TAG_OLYMPUS_MACRO_MODE);
     76        if (value==null)
     77            return null;
    6578        switch (value) {
    6679            case 0:
     
    7386    }
    7487
    75     public String getJpegQualityDescription() throws MetadataException
     88    @Nullable
     89    public String getJpegQualityDescription()
    7690    {
    77         if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_JPEG_QUALITY)) return null;
    78         int value = _directory.getInt(OlympusMakernoteDirectory.TAG_OLYMPUS_JPEG_QUALITY);
     91        Integer value = _directory.getInteger(OlympusMakernoteDirectory.TAG_OLYMPUS_JPEG_QUALITY);
     92        if (value==null)
     93            return null;
    7994        switch (value) {
    8095            case 1:
     
    89104    }
    90105
    91     public String getSpecialModeDescription() throws MetadataException
     106    @Nullable
     107    public String getSpecialModeDescription()
    92108    {
    93         if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE)) return null;
    94109        int[] values = _directory.getIntArray(OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE);
    95         StringBuffer desc = new StringBuffer();
     110        if (values==null)
     111            return null;
     112        if (values.length < 1)
     113            return "";
     114        StringBuilder desc = new StringBuilder();
    96115        switch (values[0]) {
    97116            case 0:
     
    111130                break;
    112131        }
     132
     133        if (values.length < 2)
     134            return desc.toString();
    113135        desc.append(" - ");
    114136        switch (values[1]) {
     
    117139                break;
    118140            case 1:
    119                 desc.append("1st in a sequnce");
     141                desc.append("1st in a sequence");
    120142                break;
    121143            case 2:
     
    130152                break;
    131153        }
     154        if (values.length < 3)
     155            return desc.toString();
     156        desc.append(" - ");
    132157        switch (values[2]) {
    133158            case 1:
  • trunk/src/com/drew/metadata/exif/OlympusMakernoteDirectory.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/**
    24  * The Olympus makernote is used by many manufacturers, and as such contains some tags that appear specific to
    25  * those manufacturers.  Other users include Konica, Minolta and Epson.
     29 * The Olympus makernote is used by many manufacturers (Konica, Minolta and Epson...), and as such contains some tags
     30 * that appear specific to those manufacturers.
     31 *
     32 * @author Drew Noakes http://drewnoakes.com
    2633 */
    2734public class OlympusMakernoteDirectory extends Directory
    2835{
    29     /**
    30      * Used by Konica / Minolta cameras.
    31      */
     36    /** Used by Konica / Minolta cameras. */
    3237    public static final int TAG_OLYMPUS_MAKERNOTE_VERSION = 0x0000;
    33 
    34     /**
    35      * Used by Konica / Minolta cameras.
    36      */
     38    /** Used by Konica / Minolta cameras. */
    3739    public static final int TAG_OLYMPUS_CAMERA_SETTINGS_1 = 0x0001;
    38 
    39     /**
    40      * Alternate Camera Settings Tag. Used by Konica / Minolta cameras.
    41      */
     40    /** Alternate Camera Settings Tag. Used by Konica / Minolta cameras. */
    4241    public static final int TAG_OLYMPUS_CAMERA_SETTINGS_2 = 0x0003;
    43 
    44     /**
    45      * Used by Konica / Minolta cameras.
    46      */
     42    /** Used by Konica / Minolta cameras. */
    4743    public static final int TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE = 0x0040;
    48 
    49     /**
    50      * Used by Konica / Minolta cameras.
    51      */
     44    /** Used by Konica / Minolta cameras. */
    5245    public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1 = 0x0081;
    53 
    54     /**
    55      * Alternate Thumbnail Offset. Used by Konica / Minolta cameras.
    56      */
     46    /** Alternate Thumbnail Offset. Used by Konica / Minolta cameras. */
    5747    public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2 = 0x0088;
    58 
    59     /**
    60      * Length of thumbnail in bytes. Used by Konica / Minolta cameras.
    61      */
     48    /** Length of thumbnail in bytes. Used by Konica / Minolta cameras. */
    6249    public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH = 0x0089;
    6350
     
    8471    /**
    8572     * Not 100% sure about this tag.
    86      *
     73     * <p/>
    8774     * Used by Konica / Minolta cameras.
    8875     * 0 = Raw
     
    119106    public static final int TAG_OLYMPUS_MACRO_MODE = 0x0202;
    120107
    121     /**
    122      *
    123      */
    124108    public static final int TAG_OLYMPUS_UNKNOWN_1 = 0x0203;
    125109
    126     /**
    127      * Zoom Factor (0 or 1 = normal)
    128      */
     110    /** Zoom Factor (0 or 1 = normal) */
    129111    public static final int TAG_OLYMPUS_DIGI_ZOOM_RATIO = 0x0204;
    130 
    131     /**
    132      *
    133      */
    134112    public static final int TAG_OLYMPUS_UNKNOWN_2 = 0x0205;
    135 
    136     /**
    137      *
    138      */
    139113    public static final int TAG_OLYMPUS_UNKNOWN_3 = 0x0206;
    140 
    141     /**
    142      *
    143      */
    144114    public static final int TAG_OLYMPUS_FIRMWARE_VERSION = 0x0207;
    145 
    146     /**
    147      *
    148      */
    149115    public static final int TAG_OLYMPUS_PICT_INFO = 0x0208;
    150 
    151     /**
    152      *
    153      */
    154116    public static final int TAG_OLYMPUS_CAMERA_ID = 0x0209;
    155117
     
    166128    public static final int TAG_OLYMPUS_IMAGE_HEIGHT = 0x020C;
    167129
    168     /**
    169      * A string. Used by Epson cameras.
    170      */
     130    /** A string. Used by Epson cameras. */
    171131    public static final int TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL = 0x020D;
    172132
     
    177137    public static final int TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    178138
    179     /**
    180      *
    181      */
    182139    public static final int TAG_OLYMPUS_DATA_DUMP = 0x0F00;
    183 
    184     /**
    185      *
    186      */
    187140    public static final int TAG_OLYMPUS_FLASH_MODE = 0x1004;
    188 
    189     /**
    190      *
    191      */
    192141    public static final int TAG_OLYMPUS_BRACKET = 0x1006;
    193 
    194     /**
    195      *
    196      */
    197142    public static final int TAG_OLYMPUS_FOCUS_MODE = 0x100B;
    198 
    199     /**
    200      *
    201      */
    202143    public static final int TAG_OLYMPUS_FOCUS_DISTANCE = 0x100C;
    203 
    204     /**
    205      *
    206      */
    207144    public static final int TAG_OLYMPUS_ZOOM = 0x100D;
    208 
    209     /**
    210      *
    211      */
    212145    public static final int TAG_OLYMPUS_MACRO_FOCUS = 0x100E;
    213 
    214     /**
    215      *
    216      */
    217146    public static final int TAG_OLYMPUS_SHARPNESS = 0x100F;
    218 
    219     /**
    220      *
    221      */
    222147    public static final int TAG_OLYMPUS_COLOUR_MATRIX = 0x1011;
    223 
    224     /**
    225      *
    226      */
    227148    public static final int TAG_OLYMPUS_BLACK_LEVEL = 0x1012;
    228 
    229     /**
    230      *
    231      */
    232149    public static final int TAG_OLYMPUS_WHITE_BALANCE = 0x1015;
    233 
    234     /**
    235      *
    236      */
    237150    public static final int TAG_OLYMPUS_RED_BIAS = 0x1017;
    238 
    239     /**
    240      *
    241      */
    242151    public static final int TAG_OLYMPUS_BLUE_BIAS = 0x1018;
    243 
    244     /**
    245      *
    246      */
    247152    public static final int TAG_OLYMPUS_SERIAL_NUMBER = 0x101A;
    248 
    249     /**
    250      *
    251      */
    252153    public static final int TAG_OLYMPUS_FLASH_BIAS = 0x1023;
    253 
    254     /**
    255      *
    256      */
    257154    public static final int TAG_OLYMPUS_CONTRAST = 0x1029;
    258 
    259     /**
    260      *
    261      */
    262155    public static final int TAG_OLYMPUS_SHARPNESS_FACTOR = 0x102A;
    263 
    264     /**
    265      *
    266      */
    267156    public static final int TAG_OLYMPUS_COLOUR_CONTROL = 0x102B;
    268 
    269     /**
    270      *
    271      */
    272157    public static final int TAG_OLYMPUS_VALID_BITS = 0x102C;
    273 
    274     /**
    275      *
    276      */
    277158    public static final int TAG_OLYMPUS_CORING_FILTER = 0x102D;
    278 
    279     /**
    280      *
    281      */
    282159    public static final int TAG_OLYMPUS_FINAL_WIDTH = 0x102E;
    283 
    284     /**
    285      *
    286      */
    287160    public static final int TAG_OLYMPUS_FINAL_HEIGHT = 0x102F;
    288 
    289     /**
    290      *
    291      */
    292161    public static final int TAG_OLYMPUS_COMPRESSION_RATIO = 0x1034;
    293162
    294     protected static final HashMap tagNameMap = new HashMap();
    295 
    296     static
    297     {
    298         tagNameMap.put(new Integer(TAG_OLYMPUS_SPECIAL_MODE), "Special Mode");
    299         tagNameMap.put(new Integer(TAG_OLYMPUS_JPEG_QUALITY), "Jpeg Quality");
    300         tagNameMap.put(new Integer(TAG_OLYMPUS_MACRO_MODE), "Macro");
    301         tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_1), "Makernote Unknown 1");
    302         tagNameMap.put(new Integer(TAG_OLYMPUS_DIGI_ZOOM_RATIO), "DigiZoom Ratio");
    303         tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_2), "Makernote Unknown 2");
    304         tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_3), "Makernote Unknown 3");
    305         tagNameMap.put(new Integer(TAG_OLYMPUS_FIRMWARE_VERSION), "Firmware Version");
    306         tagNameMap.put(new Integer(TAG_OLYMPUS_PICT_INFO), "Pict Info");
    307         tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_ID), "Camera Id");
    308         tagNameMap.put(new Integer(TAG_OLYMPUS_DATA_DUMP), "Data Dump");
    309         tagNameMap.put(new Integer(TAG_OLYMPUS_MAKERNOTE_VERSION), "Makernote Version");
    310         tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_SETTINGS_1), "Camera Settings");
    311         tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_SETTINGS_2), "Camera Settings");
    312         tagNameMap.put(new Integer(TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE), "Compressed Image Size");
    313         tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1), "Thumbnail Offset");
    314         tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2), "Thumbnail Offset");
    315         tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH), "Thumbnail Length");
    316         tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_MODE), "Colour Mode");
    317         tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_QUALITY_1), "Image Quality");
    318         tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_QUALITY_2), "Image Quality");
    319         tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_HEIGHT), "Image Height");
    320         tagNameMap.put(new Integer(TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL), "Original Manufacturer Model");
    321         tagNameMap.put(new Integer(TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");
    322         tagNameMap.put(new Integer(TAG_OLYMPUS_FLASH_MODE), "Flash Mode");
    323         tagNameMap.put(new Integer(TAG_OLYMPUS_BRACKET), "Bracket");
    324         tagNameMap.put(new Integer(TAG_OLYMPUS_FOCUS_MODE), "Focus Mode");
    325         tagNameMap.put(new Integer(TAG_OLYMPUS_FOCUS_DISTANCE), "Focus Distance");
    326         tagNameMap.put(new Integer(TAG_OLYMPUS_ZOOM), "Zoom");
    327         tagNameMap.put(new Integer(TAG_OLYMPUS_MACRO_FOCUS), "Macro Focus");
    328         tagNameMap.put(new Integer(TAG_OLYMPUS_SHARPNESS), "Sharpness");
    329         tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_MATRIX), "Colour Matrix");
    330         tagNameMap.put(new Integer(TAG_OLYMPUS_BLACK_LEVEL), "Black Level");
    331         tagNameMap.put(new Integer(TAG_OLYMPUS_WHITE_BALANCE), "White Balance");
    332         tagNameMap.put(new Integer(TAG_OLYMPUS_RED_BIAS), "Red Bias");
    333         tagNameMap.put(new Integer(TAG_OLYMPUS_BLUE_BIAS), "Blue Bias");
    334         tagNameMap.put(new Integer(TAG_OLYMPUS_SERIAL_NUMBER), "Serial Number");
    335         tagNameMap.put(new Integer(TAG_OLYMPUS_FLASH_BIAS), "Flash Bias");
    336         tagNameMap.put(new Integer(TAG_OLYMPUS_CONTRAST), "Contrast");
    337         tagNameMap.put(new Integer(TAG_OLYMPUS_SHARPNESS_FACTOR), "Sharpness Factor");
    338         tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_CONTROL), "Colour Control");
    339         tagNameMap.put(new Integer(TAG_OLYMPUS_VALID_BITS), "Valid Bits");
    340         tagNameMap.put(new Integer(TAG_OLYMPUS_CORING_FILTER), "Coring Filter");
    341         tagNameMap.put(new Integer(TAG_OLYMPUS_FINAL_WIDTH), "Final Width");
    342         tagNameMap.put(new Integer(TAG_OLYMPUS_FINAL_HEIGHT), "Final Height");
    343         tagNameMap.put(new Integer(TAG_OLYMPUS_COMPRESSION_RATIO), "Compression Ratio");
     163    @NotNull
     164    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     165
     166    static {
     167        _tagNameMap.put(TAG_OLYMPUS_SPECIAL_MODE, "Special Mode");
     168        _tagNameMap.put(TAG_OLYMPUS_JPEG_QUALITY, "Jpeg Quality");
     169        _tagNameMap.put(TAG_OLYMPUS_MACRO_MODE, "Macro");
     170        _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_1, "Makernote Unknown 1");
     171        _tagNameMap.put(TAG_OLYMPUS_DIGI_ZOOM_RATIO, "DigiZoom Ratio");
     172        _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_2, "Makernote Unknown 2");
     173        _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_3, "Makernote Unknown 3");
     174        _tagNameMap.put(TAG_OLYMPUS_FIRMWARE_VERSION, "Firmware Version");
     175        _tagNameMap.put(TAG_OLYMPUS_PICT_INFO, "Pict Info");
     176        _tagNameMap.put(TAG_OLYMPUS_CAMERA_ID, "Camera Id");
     177        _tagNameMap.put(TAG_OLYMPUS_DATA_DUMP, "Data Dump");
     178        _tagNameMap.put(TAG_OLYMPUS_MAKERNOTE_VERSION, "Makernote Version");
     179        _tagNameMap.put(TAG_OLYMPUS_CAMERA_SETTINGS_1, "Camera Settings");
     180        _tagNameMap.put(TAG_OLYMPUS_CAMERA_SETTINGS_2, "Camera Settings");
     181        _tagNameMap.put(TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE, "Compressed Image Size");
     182        _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1, "Thumbnail Offset");
     183        _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2, "Thumbnail Offset");
     184        _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH, "Thumbnail Length");
     185        _tagNameMap.put(TAG_OLYMPUS_COLOUR_MODE, "Colour Mode");
     186        _tagNameMap.put(TAG_OLYMPUS_IMAGE_QUALITY_1, "Image Quality");
     187        _tagNameMap.put(TAG_OLYMPUS_IMAGE_QUALITY_2, "Image Quality");
     188        _tagNameMap.put(TAG_OLYMPUS_IMAGE_HEIGHT, "Image Height");
     189        _tagNameMap.put(TAG_OLYMPUS_IMAGE_WIDTH, "Image Width");
     190        _tagNameMap.put(TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL, "Original Manufacturer Model");
     191        _tagNameMap.put(TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
     192        _tagNameMap.put(TAG_OLYMPUS_FLASH_MODE, "Flash Mode");
     193        _tagNameMap.put(TAG_OLYMPUS_BRACKET, "Bracket");
     194        _tagNameMap.put(TAG_OLYMPUS_FOCUS_MODE, "Focus Mode");
     195        _tagNameMap.put(TAG_OLYMPUS_FOCUS_DISTANCE, "Focus Distance");
     196        _tagNameMap.put(TAG_OLYMPUS_ZOOM, "Zoom");
     197        _tagNameMap.put(TAG_OLYMPUS_MACRO_FOCUS, "Macro Focus");
     198        _tagNameMap.put(TAG_OLYMPUS_SHARPNESS, "Sharpness");
     199        _tagNameMap.put(TAG_OLYMPUS_COLOUR_MATRIX, "Colour Matrix");
     200        _tagNameMap.put(TAG_OLYMPUS_BLACK_LEVEL, "Black Level");
     201        _tagNameMap.put(TAG_OLYMPUS_WHITE_BALANCE, "White Balance");
     202        _tagNameMap.put(TAG_OLYMPUS_RED_BIAS, "Red Bias");
     203        _tagNameMap.put(TAG_OLYMPUS_BLUE_BIAS, "Blue Bias");
     204        _tagNameMap.put(TAG_OLYMPUS_SERIAL_NUMBER, "Serial Number");
     205        _tagNameMap.put(TAG_OLYMPUS_FLASH_BIAS, "Flash Bias");
     206        _tagNameMap.put(TAG_OLYMPUS_CONTRAST, "Contrast");
     207        _tagNameMap.put(TAG_OLYMPUS_SHARPNESS_FACTOR, "Sharpness Factor");
     208        _tagNameMap.put(TAG_OLYMPUS_COLOUR_CONTROL, "Colour Control");
     209        _tagNameMap.put(TAG_OLYMPUS_VALID_BITS, "Valid Bits");
     210        _tagNameMap.put(TAG_OLYMPUS_CORING_FILTER, "Coring Filter");
     211        _tagNameMap.put(TAG_OLYMPUS_FINAL_WIDTH, "Final Width");
     212        _tagNameMap.put(TAG_OLYMPUS_FINAL_HEIGHT, "Final Height");
     213        _tagNameMap.put(TAG_OLYMPUS_COMPRESSION_RATIO, "Compression Ratio");
    344214    }
    345215
     
    349219    }
    350220
     221    @NotNull
    351222    public String getName()
    352223    {
     
    354225    }
    355226
    356     protected HashMap getTagNameMap()
     227    @NotNull
     228    protected HashMap<Integer, String> getTagNameMap()
    357229    {
    358         return tagNameMap;
     230        return _tagNameMap;
    359231    }
    360232}
  • trunk/src/com/drew/metadata/exif/PanasonicMakernoteDescriptor.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
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.ByteArrayReader;
     26import com.drew.lang.annotations.NotNull;
     27import com.drew.lang.annotations.Nullable;
     28import com.drew.metadata.Age;
     29import com.drew.metadata.Face;
    1930import com.drew.metadata.TagDescriptor;
    2031
     32import java.io.UnsupportedEncodingException;
     33
    2134/**
    22  * Provides human-readable string versions of the tags stored in a PanasonicMakernoteDirectory.
     35 * Provides human-readable string representations of tag values stored in a <code>PanasonicMakernoteDirectory</code>.
     36 * <p/>
     37 * Some information about this makernote taken from here:
     38 * <ul>
     39 * <li><a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html">http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html</a></li>
     40 * <li><a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html">http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html</a></li>
     41 * </ul>
    2342 *
    24  * Some information about this makernote taken from here:
    25  * http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
     43 * @author Drew Noakes http://drewnoakes.com, Philipp Sandhaus
    2644 */
    27 public class PanasonicMakernoteDescriptor extends TagDescriptor
     45public class PanasonicMakernoteDescriptor extends TagDescriptor<PanasonicMakernoteDirectory>
    2846{
    29     public PanasonicMakernoteDescriptor(Directory directory)
     47    public PanasonicMakernoteDescriptor(@NotNull PanasonicMakernoteDirectory directory)
    3048    {
    3149        super(directory);
    3250    }
    3351
    34     public String getDescription(int tagType) throws MetadataException
    35     {
    36         switch (tagType)
     52    @Nullable
     53    public String getDescription(int tagType)
     54    {
     55        switch (tagType) {
     56            case PanasonicMakernoteDirectory.TAG_QUALITY_MODE:
     57                return getQualityModeDescription();
     58            case PanasonicMakernoteDirectory.TAG_VERSION:
     59                return getVersionDescription();
     60            case PanasonicMakernoteDirectory.TAG_WHITE_BALANCE:
     61                return getWhiteBalanceDescription();
     62            case PanasonicMakernoteDirectory.TAG_FOCUS_MODE:
     63                return getFocusModeDescription();
     64            case PanasonicMakernoteDirectory.TAG_AF_AREA_MODE:
     65                return getAfAreaModeDescription();
     66            case PanasonicMakernoteDirectory.TAG_IMAGE_STABILIZATION:
     67                return getImageStabilizationDescription();
     68            case PanasonicMakernoteDirectory.TAG_MACRO_MODE:
     69                return getMacroModeDescription();
     70            case PanasonicMakernoteDirectory.TAG_RECORD_MODE:
     71                return getRecordModeDescription();
     72            case PanasonicMakernoteDirectory.TAG_AUDIO:
     73                return getAudioDescription();
     74            case PanasonicMakernoteDirectory.TAG_UNKNOWN_DATA_DUMP:
     75                return getUnknownDataDumpDescription();
     76            case PanasonicMakernoteDirectory.TAG_COLOR_EFFECT:
     77                return getColorEffectDescription();
     78            case PanasonicMakernoteDirectory.TAG_UPTIME:
     79                return getUptimeDescription();
     80            case PanasonicMakernoteDirectory.TAG_BURST_MODE:
     81                return getBurstModeDescription();
     82            case PanasonicMakernoteDirectory.TAG_CONTRAST_MODE:
     83                return getContrastModeDescription();
     84            case PanasonicMakernoteDirectory.TAG_NOISE_REDUCTION:
     85                return getNoiseReductionDescription();
     86            case PanasonicMakernoteDirectory.TAG_SELF_TIMER:
     87                return getSelfTimerDescription();
     88            case PanasonicMakernoteDirectory.TAG_ROTATION:
     89                return getRotationDescription();
     90            case PanasonicMakernoteDirectory.TAG_AF_ASSIST_LAMP:
     91                return getAfAssistLampDescription();
     92            case PanasonicMakernoteDirectory.TAG_COLOR_MODE:
     93                return getColorModeDescription();
     94            case PanasonicMakernoteDirectory.TAG_OPTICAL_ZOOM_MODE:
     95                return getOpticalZoomModeDescription();
     96            case PanasonicMakernoteDirectory.TAG_CONVERSION_LENS:
     97                return getConversionLensDescription();
     98            case PanasonicMakernoteDirectory.TAG_CONTRAST:
     99                return getContrastDescription();
     100            case PanasonicMakernoteDirectory.TAG_WORLD_TIME_LOCATION:
     101                return getWorldTimeLocationDescription();
     102            case PanasonicMakernoteDirectory.TAG_ADVANCED_SCENE_MODE:
     103                return getAdvancedSceneModeDescription();
     104            case PanasonicMakernoteDirectory.TAG_FACE_DETECTION_INFO:
     105                return getDetectedFacesDescription();
     106            case PanasonicMakernoteDirectory.TAG_TRANSFORM:
     107                return getTransformDescription();
     108                        case PanasonicMakernoteDirectory.TAG_TRANSFORM_1:
     109                    return getTransform1Description();
     110            case PanasonicMakernoteDirectory.TAG_INTELLIGENT_EXPOSURE:
     111                return getIntelligentExposureDescription();
     112            case PanasonicMakernoteDirectory.TAG_FLASH_WARNING:
     113                return getFlashWarningDescription();
     114            case PanasonicMakernoteDirectory.TAG_COUNTRY:
     115                return getCountryDescription();
     116            case PanasonicMakernoteDirectory.TAG_STATE:
     117                return getStateDescription();
     118            case PanasonicMakernoteDirectory.TAG_CITY:
     119                return getCityDescription();
     120            case PanasonicMakernoteDirectory.TAG_LANDMARK:
     121                return getLandmarkDescription();
     122            case PanasonicMakernoteDirectory.TAG_INTELLIGENT_RESOLUTION:
     123                return getIntelligentResolutionDescription();
     124            case PanasonicMakernoteDirectory.TAG_FACE_RECOGNITION_INFO:
     125                return getRecognizedFacesDescription();
     126            case PanasonicMakernoteDirectory.TAG_PRINT_IMAGE_MATCHING_INFO:
     127                return getPrintImageMatchingInfoDescription();
     128            case PanasonicMakernoteDirectory.TAG_SCENE_MODE:
     129                return getSceneModeDescription();
     130            case PanasonicMakernoteDirectory.TAG_FLASH_FIRED:
     131                return getFlashFiredDescription();
     132            case PanasonicMakernoteDirectory.TAG_TEXT_STAMP:
     133                        return getTextStampDescription();
     134                        case PanasonicMakernoteDirectory.TAG_TEXT_STAMP_1:
     135                     return getTextStamp1Description();
     136                        case PanasonicMakernoteDirectory.TAG_TEXT_STAMP_2:
     137                         return getTextStamp2Description();
     138                        case PanasonicMakernoteDirectory.TAG_TEXT_STAMP_3:
     139                             return getTextStamp3Description();
     140            case PanasonicMakernoteDirectory.TAG_MAKERNOTE_VERSION:
     141                return getMakernoteVersionDescription();
     142            case PanasonicMakernoteDirectory.TAG_EXIF_VERSION:
     143                return getExifVersionDescription();
     144            case PanasonicMakernoteDirectory.TAG_INTERNAL_SERIAL_NUMBER:
     145                return getInternalSerialNumberDescription();
     146            case PanasonicMakernoteDirectory.TAG_TITLE:
     147                    return getTitleDescription();
     148                        case PanasonicMakernoteDirectory.TAG_BABY_NAME:
     149                    return getBabyNameDescription();
     150                        case PanasonicMakernoteDirectory.TAG_LOCATION:
     151                    return getLocationDescription();
     152                        case PanasonicMakernoteDirectory.TAG_BABY_AGE:
     153                        return getBabyAgeDescription();
     154                        case PanasonicMakernoteDirectory.TAG_BABY_AGE_1:
     155                        return getBabyAge1Description();
     156                        default:
     157                return super.getDescription(tagType);
     158        }
     159    }
     160
     161    @Nullable
     162    public String getPrintImageMatchingInfoDescription()
     163    {
     164        byte[] values = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_PRINT_IMAGE_MATCHING_INFO);
     165        if (values == null)
     166            return null;
     167        return "(" + values.length + " bytes)";
     168    }
     169
     170    @Nullable
     171    public String getTextStampDescription()
     172    {
     173        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP);
     174    }
     175
     176        @Nullable
     177    public String getTextStamp1Description()
     178    {
     179        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP_1);
     180    }
     181
     182        @Nullable
     183    public String getTextStamp2Description()
     184    {
     185        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP_2);
     186    }
     187
     188        @Nullable
     189    public String getTextStamp3Description()
     190    {
     191        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP_3);
     192    }
     193
     194        @Nullable
     195    public String getMacroModeDescription()
     196    {
     197        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_MACRO_MODE);
     198    }
     199
     200    @Nullable
     201    public String getFlashFiredDescription()
     202    {
     203        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_FLASH_FIRED);
     204    }
     205
     206    @Nullable
     207    public String getImageStabilizationDescription()
     208    {
     209        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_IMAGE_STABILIZATION);
     210        if (value == null)
     211            return null;
     212        switch (value) {
     213            case 2:
     214                return "On, Mode 1";
     215            case 3:
     216                return "Off";
     217            case 4:
     218                return "On, Mode 2";
     219            default:
     220                return "Unknown (" + value + ")";
     221        }
     222    }
     223
     224    @Nullable
     225    public String getAudioDescription()
     226    {
     227        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_AUDIO);
     228    }
     229
     230    @Nullable
     231    public String getTransformDescription()
     232    {
     233        return getTransformDescription(PanasonicMakernoteDirectory.TAG_TRANSFORM);
     234    }
     235
     236    @Nullable
     237    public String getTransform1Description()
     238    {
     239        return getTransformDescription(PanasonicMakernoteDirectory.TAG_TRANSFORM_1);
     240    }
     241
     242    @Nullable
     243    private String getTransformDescription(int tag)
     244    {
     245        byte[] values = _directory.getByteArray(tag);
     246        if (values == null)
     247            return null;
     248
     249        BufferReader reader = new ByteArrayReader(values);
     250
     251        try
    37252        {
    38             case PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE:
    39                 return getMacroModeDescription();
    40             case PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE:
    41                 return getRecordModeDescription();
    42             case PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO:
    43                 return getPrintImageMatchingInfoDescription();
    44             default:
    45                 return _directory.getString(tagType);
    46         }
    47     }
    48 
    49     public String getPrintImageMatchingInfoDescription() throws MetadataException
    50     {
    51         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null;
    52         byte[] bytes = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO);
    53         return "(" + bytes.length + " bytes)";
    54     }
    55 
    56     public String getMacroModeDescription() throws MetadataException
    57     {
    58         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null;
    59         int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
    60         switch (value) {
    61             case 1:
     253            int val1 = reader.getUInt16(0);
     254            int val2 = reader.getUInt16(2);
     255
     256            if (val1 == -1 && val2 == 1)
     257                return "Slim Low";
     258            if (val1 == -3 && val2 == 2)
     259                return "Slim High";
     260            if (val1 == 0 && val2 == 0)
     261                return "Off";
     262            if (val1 == 1 && val2 == 1)
     263                return "Stretch Low";
     264            if (val1 == 3 && val2 == 2)
     265                return "Stretch High";
     266
     267            return "Unknown (" + val1 + " " + val2 + ")";
     268        } catch (BufferBoundsException e) {
     269            return null   ;
     270        }
     271    }
     272
     273    @Nullable
     274    public String getIntelligentExposureDescription()
     275    {
     276        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_INTELLIGENT_EXPOSURE);
     277        if (value == null)
     278            return null;
     279        switch (value) {
     280            case 0:
     281                return "Off";
     282            case 1:
     283                return "Low";
     284            case 2:
     285                return "Standard";
     286            case 3:
     287                return "High";
     288
     289            default:
     290                return "Unknown (" + value + ")";
     291        }
     292    }
     293
     294    @Nullable
     295    public String getFlashWarningDescription()
     296    {
     297        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_FLASH_WARNING);
     298        if (value == null)
     299            return null;
     300        switch (value) {
     301            case 0:
     302                return "No";
     303            case 1:
     304                return "Yes (Flash required but disabled)";
     305            default:
     306                return "Unknown (" + value + ")";
     307        }
     308    }
     309       
     310    @Nullable
     311    public String getCountryDescription()
     312    {
     313        return getTextDescription(PanasonicMakernoteDirectory.TAG_COUNTRY);
     314    }
     315
     316    @Nullable
     317    public String getStateDescription()
     318    {
     319        return getTextDescription(PanasonicMakernoteDirectory.TAG_STATE);
     320    }
     321
     322    @Nullable
     323    public String getCityDescription()
     324    {
     325        return getTextDescription(PanasonicMakernoteDirectory.TAG_CITY);
     326    }
     327
     328    @Nullable
     329    public String getLandmarkDescription()
     330    {
     331        return getTextDescription(PanasonicMakernoteDirectory.TAG_LANDMARK);
     332    }
     333
     334        @Nullable
     335    public String getTitleDescription()
     336    {
     337        return getTextDescription(PanasonicMakernoteDirectory.TAG_TITLE);
     338    }
     339
     340        @Nullable
     341    public String getBabyNameDescription()
     342    {
     343        return getTextDescription(PanasonicMakernoteDirectory.TAG_BABY_NAME);
     344    }
     345
     346        @Nullable
     347    public String getLocationDescription()
     348    {
     349        return getTextDescription(PanasonicMakernoteDirectory.TAG_LOCATION);
     350    }
     351
     352    @Nullable
     353    public String getIntelligentResolutionDescription()
     354    {
     355        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_INTELLIGENT_RESOLUTION);
     356        if (value == null)
     357            return null;
     358        switch (value) {
     359            case 0:
     360                return "Off";
     361            case 2:
     362                return "Auto";
     363            case 3:
    62364                return "On";
    63             case 2:
     365            default:
     366                return "Unknown (" + value + ")";
     367        }
     368    }
     369
     370    @Nullable
     371    public String getContrastDescription()
     372    {
     373        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONTRAST);
     374        if (value == null)
     375            return null;
     376        switch (value) {
     377            case 0:
     378                return "Normal";
     379            default:
     380                return "Unknown (" + value + ")";
     381        }
     382    }
     383
     384    @Nullable
     385    public String getWorldTimeLocationDescription()
     386    {
     387        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_WORLD_TIME_LOCATION);
     388        if (value == null)
     389            return null;
     390        switch (value) {
     391            case 1:
     392                return "Home";
     393            case 2:
     394                return "Destination";
     395            default:
     396                return "Unknown (" + value + ")";
     397        }
     398    }
     399
     400    @Nullable
     401    public String getAdvancedSceneModeDescription()
     402    {
     403        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_ADVANCED_SCENE_MODE);
     404        if (value == null)
     405            return null;
     406        switch (value) {
     407            case 1:
     408                return "Normal";
     409            case 2:
     410                return "Outdoor/Illuminations/Flower/HDR Art";
     411            case 3:
     412                return "Indoor/Architecture/Objects/HDR B&W";
     413            case 4:
     414                return "Creative";
     415            case 5:
     416                return "Auto";
     417            case 7:
     418                return "Expressive";
     419            case 8:
     420                return "Retro";
     421            case 9:
     422                return "Pure";
     423            case 10:
     424                return "Elegant";
     425            case 12:
     426                return "Monochrome";
     427            case 13:
     428                return "Dynamic Art";
     429            case 14:
     430                return "Silhouette";
     431            default:
     432                return "Unknown (" + value + ")";
     433        }
     434    }
     435
     436    @Nullable
     437    public String getUnknownDataDumpDescription()
     438    {
     439        byte[] value = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_UNKNOWN_DATA_DUMP);
     440        if (value == null)
     441            return null;
     442        return "[" + value.length + " bytes]";
     443    }
     444
     445    @Nullable
     446    public String getColorEffectDescription()
     447    {
     448        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_COLOR_EFFECT);
     449        if (value == null)
     450            return null;
     451        switch (value) {
     452            case 1:
    64453                return "Off";
    65             default:
    66                 return "Unknown (" + value + ")";
    67         }
    68     }
    69 
    70     public String getRecordModeDescription() throws MetadataException
    71     {
    72         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null;
    73         int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
    74         switch (value) {
    75             case 1:
     454            case 2:
     455                return "Warm";
     456            case 3:
     457                return "Cool";
     458            case 4:
     459                return "Black & White";
     460            case 5:
     461                return "Sepia";
     462            default:
     463                return "Unknown (" + value + ")";
     464        }
     465    }
     466
     467    @Nullable
     468    public String getUptimeDescription()
     469    {
     470        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_UPTIME);
     471        if (value == null)
     472            return null;
     473        return value / 100f + " s";
     474    }
     475
     476    @Nullable
     477    public String getBurstModeDescription()
     478    {
     479        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_BURST_MODE);
     480        if (value == null)
     481            return null;
     482        switch (value) {
     483            case 0:
     484                return "Off";
     485            case 1:
     486                return "On";
     487            case 2:
     488                return "Infinite";
     489            case 4:
     490                return "Unlimited";
     491            default:
     492                return "Unknown (" + value + ")";
     493        }
     494    }
     495
     496    @Nullable
     497    public String getContrastModeDescription()
     498    {
     499        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONTRAST_MODE);
     500        if (value == null)
     501            return null;
     502        switch (value) {
     503            case 0x0:
    76504                return "Normal";
     505            case 0x1:
     506                return "Low";
     507            case 0x2:
     508                return "High";
     509            case 0x6:
     510                return "Medium Low";
     511            case 0x7:
     512                return "Medium High";
     513            case 0x100:
     514                return "Low";
     515            case 0x110:
     516                return "Normal";
     517            case 0x120:
     518                return "High";
     519
     520            default:
     521                return "Unknown (" + value + ")";
     522        }
     523    }
     524
     525    @Nullable
     526    public String getNoiseReductionDescription()
     527    {
     528        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_NOISE_REDUCTION);
     529        if (value == null)
     530            return null;
     531        switch (value) {
     532            case 0:
     533                return "Standard (0)";
     534            case 1:
     535                return "Low (-1)";
     536            case 2:
     537                return "High (+1)";
     538            case 3:
     539                return "Lowest (-2)";
     540            case 4:
     541                return "Highest (+2)";
     542            default:
     543                return "Unknown (" + value + ")";
     544        }
     545    }
     546
     547
     548    @Nullable
     549    public String getSelfTimerDescription()
     550    {
     551        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_SELF_TIMER);
     552        if (value == null)
     553            return null;
     554        switch (value) {
     555            case 1:
     556                return "Off";
     557            case 2:
     558                return "10 s";
     559            case 3:
     560                return "2 s";
     561            default:
     562                return "Unknown (" + value + ")";
     563        }
     564    }
     565
     566    @Nullable
     567    public String getRotationDescription()
     568    {
     569        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_ROTATION);
     570        if (value == null)
     571            return null;
     572        switch (value) {
     573            case 1:
     574                return "Horizontal";
     575            case 3:
     576                return "Rotate 180";
     577            case 6:
     578                return "Rotate 90 CW";
     579            case 8:
     580                return "Rotate 270 CW";
     581            default:
     582                return "Unknown (" + value + ")";
     583        }
     584    }
     585
     586    @Nullable
     587    public String getAfAssistLampDescription()
     588    {
     589        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_AF_ASSIST_LAMP);
     590        if (value == null)
     591            return null;
     592        switch (value) {
     593            case 1:
     594                return "Fired";
     595            case 2:
     596                return "Enabled but not used";
     597            case 3:
     598                return "Disabled but required";
     599            case 4:
     600                return "Disabled and not required";
     601            default:
     602                return "Unknown (" + value + ")";
     603        }
     604    }
     605
     606    @Nullable
     607    public String getColorModeDescription()
     608    {
     609        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_COLOR_MODE);
     610        if (value == null)
     611            return null;
     612        switch (value) {
     613            case 0:
     614                return "Normal";
     615            case 1:
     616                return "Natural";
     617            case 2:
     618                return "Vivid";
     619            default:
     620                return "Unknown (" + value + ")";
     621        }
     622    }
     623
     624    @Nullable
     625    public String getOpticalZoomModeDescription()
     626    {
     627        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_OPTICAL_ZOOM_MODE);
     628        if (value == null)
     629            return null;
     630        switch (value) {
     631            case 1:
     632                return "Standard";
     633            case 2:
     634                return "Extended";
     635            default:
     636                return "Unknown (" + value + ")";
     637        }
     638    }
     639
     640    @Nullable
     641    public String getConversionLensDescription()
     642    {
     643        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONVERSION_LENS);
     644        if (value == null)
     645            return null;
     646        switch (value) {
     647            case 1:
     648                return "Off";
     649            case 2:
     650                return "Wide";
     651            case 3:
     652                return "Telephoto";
     653            case 4:
     654                return "Macro";
     655            default:
     656                return "Unknown (" + value + ")";
     657        }
     658    }
     659
     660    @Nullable
     661    public String getDetectedFacesDescription()
     662    {
     663        return buildFacesDescription(_directory.getDetectedFaces());
     664    }
     665
     666    @Nullable
     667    public String getRecognizedFacesDescription()
     668    {
     669        return buildFacesDescription(_directory.getRecognizedFaces());
     670    }
     671
     672    @Nullable
     673    private String buildFacesDescription(@Nullable Face[] faces)
     674    {
     675        if (faces == null)
     676            return null;
     677
     678        StringBuilder result = new StringBuilder();
     679
     680        for (int i = 0; i < faces.length; i++)
     681            result.append("Face ").append(i + 1).append(": ").append(faces[i].toString()).append("\n");
     682
     683        if (result.length() > 0)
     684            return result.substring(0, result.length() - 1);
     685
     686        return null;
     687    }
     688
     689    @Nullable
     690    public String getRecordModeDescription()
     691    {
     692        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_RECORD_MODE);
     693        if (value == null)
     694            return null;
     695        switch (value) {
     696            case 1:
     697                return "Normal";
    77698            case 2:
    78699                return "Portrait";
     700            case 3:
     701                return "Scenery";
     702            case 4:
     703                return "Sports";
     704            case 5:
     705                return "Night Portrait";
     706            case 6:
     707                return "Program";
     708            case 7:
     709                return "Aperture Priority";
     710            case 8:
     711                return "Shutter Priority";
    79712            case 9:
    80713                return "Macro";
     714            case 10:
     715                return "Spot";
     716            case 11:
     717                return "Manual";
     718            case 12:
     719                return "Movie Preview";
     720            case 13:
     721                return "Panning";
     722            case 14:
     723                return "Simple";
     724            case 15:
     725                return "Color Effects";
     726            case 16:
     727                return "Self Portrait";
     728            case 17:
     729                return "Economy";
     730            case 18:
     731                return "Fireworks";
     732            case 19:
     733                return "Party";
     734            case 20:
     735                return "Snow";
     736            case 21:
     737                return "Night Scenery";
     738            case 22:
     739                return "Food";
     740            case 23:
     741                return "Baby";
     742            case 24:
     743                return "Soft Skin";
     744            case 25:
     745                return "Candlelight";
     746            case 26:
     747                return "Starry Night";
     748            case 27:
     749                return "High Sensitivity";
     750            case 28:
     751                return "Panorama Assist";
     752            case 29:
     753                return "Underwater";
     754            case 30:
     755                return "Beach";
     756            case 31:
     757                return "Aerial Photo";
     758            case 32:
     759                return "Sunset";
     760            case 33:
     761                return "Pet";
     762            case 34:
     763                return "Intelligent ISO";
     764            case 35:
     765                return "Clipboard";
     766            case 36:
     767                return "High Speed Continuous Shooting";
     768            case 37:
     769                return "Intelligent Auto";
     770            case 39:
     771                return "Multi-aspect";
     772            case 41:
     773                return "Transform";
     774            case 42:
     775                return "Flash Burst";
     776            case 43:
     777                return "Pin Hole";
     778            case 44:
     779                return "Film Grain";
     780            case 45:
     781                return "My Color";
     782            case 46:
     783                return "Photo Frame";
     784            case 51:
     785                return "HDR";
     786            default:
     787                return "Unknown (" + value + ")";
     788        }
     789    }
     790
     791    @Nullable
     792    public String getSceneModeDescription()
     793    {
     794        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_SCENE_MODE);
     795        if (value == null)
     796            return null;
     797        switch (value) {
     798            case 1:
     799                return "Normal";
     800            case 2:
     801                return "Portrait";
     802            case 3:
     803                return "Scenery";
     804            case 4:
     805                return "Sports";
     806            case 5:
     807                return "Night Portrait";
     808            case 6:
     809                return "Program";
     810            case 7:
     811                return "Aperture Priority";
     812            case 8:
     813                return "Shutter Priority";
     814            case 9:
     815                return "Macro";
     816            case 10:
     817                return "Spot";
     818            case 11:
     819                return "Manual";
     820            case 12:
     821                return "Movie Preview";
     822            case 13:
     823                return "Panning";
     824            case 14:
     825                return "Simple";
     826            case 15:
     827                return "Color Effects";
     828            case 16:
     829                return "Self Portrait";
     830            case 17:
     831                return "Economy";
     832            case 18:
     833                return "Fireworks";
     834            case 19:
     835                return "Party";
     836            case 20:
     837                return "Snow";
     838            case 21:
     839                return "Night Scenery";
     840            case 22:
     841                return "Food";
     842            case 23:
     843                return "Baby";
     844            case 24:
     845                return "Soft Skin";
     846            case 25:
     847                return "Candlelight";
     848            case 26:
     849                return "Starry Night";
     850            case 27:
     851                return "High Sensitivity";
     852            case 28:
     853                return "Panorama Assist";
     854            case 29:
     855                return "Underwater";
     856            case 30:
     857                return "Beach";
     858            case 31:
     859                return "Aerial Photo";
     860            case 32:
     861                return "Sunset";
     862            case 33:
     863                return "Pet";
     864            case 34:
     865                return "Intelligent ISO";
     866            case 35:
     867                return "Clipboard";
     868            case 36:
     869                return "High Speed Continuous Shooting";
     870            case 37:
     871                return "Intelligent Auto";
     872            case 39:
     873                return "Multi-aspect";
     874            case 41:
     875                return "Transform";
     876            case 42:
     877                return "Flash Burst";
     878            case 43:
     879                return "Pin Hole";
     880            case 44:
     881                return "Film Grain";
     882            case 45:
     883                return "My Color";
     884            case 46:
     885                return "Photo Frame";
     886            case 51:
     887                return "HDR";
     888            default:
     889                return "Unknown (" + value + ")";
     890        }
     891    }
     892
     893    @Nullable
     894    public String getFocusModeDescription()
     895    {
     896        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_FOCUS_MODE);
     897        if (value == null)
     898            return null;
     899        switch (value) {
     900            case 1:
     901                return "Auto";
     902            case 2:
     903                return "Manual";
     904            case 4:
     905                return "Auto, Focus Button";
     906            case 5:
     907                return "Auto, Continuous";
     908            default:
     909                return "Unknown (" + value + ")";
     910        }
     911    }
     912
     913    @Nullable
     914    public String getAfAreaModeDescription()
     915    {
     916        int[] value = _directory.getIntArray(PanasonicMakernoteDirectory.TAG_AF_AREA_MODE);
     917        if (value == null || value.length < 2)
     918            return null;
     919        switch (value[0]) {
     920            case 0:
     921                switch (value[1]) {
     922                    case 1:
     923                        return "Spot Mode On";
     924                    case 16:
     925                        return "Spot Mode Off";
     926                    default:
     927                        return "Unknown (" + value[0] + " " + value[1] + ")";
     928                }
     929            case 1:
     930                switch (value[1]) {
     931                    case 0:
     932                        return "Spot Focusing";
     933                    case 1:
     934                        return "5-area";
     935                    default:
     936                        return "Unknown (" + value[0] + " " + value[1] + ")";
     937                }
     938            case 16:
     939                switch (value[1]) {
     940                    case 0:
     941                        return "1-area";
     942                    case 16:
     943                        return "1-area (high speed)";
     944                    default:
     945                        return "Unknown (" + value[0] + " " + value[1] + ")";
     946                }
     947            case 32:
     948                switch (value[1]) {
     949                    case 0:
     950                        return "Auto or Face Detect";
     951                    case 1:
     952                        return "3-area (left)";
     953                    case 2:
     954                        return "3-area (center)";
     955                    case 3:
     956                        return "3-area (right)";
     957                    default:
     958                        return "Unknown (" + value[0] + " " + value[1] + ")";
     959                }
     960            case 64:
     961                return "Face Detect";
     962            default:
     963                return "Unknown (" + value[0] + " " + value[1] + ")";
     964        }
     965    }
     966
     967    @Nullable
     968    public String getQualityModeDescription()
     969    {
     970        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_QUALITY_MODE);
     971        if (value == null)
     972            return null;
     973        switch (value) {
     974            case 2:
     975                return "High";
     976            case 3:
     977                return "Normal";
     978            case 6:
     979                return "Very High";
     980            case 7:
     981                return "Raw";
     982            case 9:
     983                return "Motion Picture";
     984            default:
     985                return "Unknown (" + value + ")";
     986        }
     987    }
     988
     989    @Nullable
     990    public String getVersionDescription()
     991    {
     992        return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_VERSION), 2);
     993    }
     994
     995    @Nullable
     996    public String getMakernoteVersionDescription()
     997    {
     998        return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_MAKERNOTE_VERSION), 2);
     999    }
     1000
     1001    @Nullable
     1002    public String getExifVersionDescription()
     1003    {
     1004        return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_EXIF_VERSION), 2);
     1005    }
     1006
     1007    @Nullable
     1008    public String getInternalSerialNumberDescription()
     1009    {
     1010        final byte[] bytes = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_INTERNAL_SERIAL_NUMBER);
     1011
     1012        if (bytes==null)
     1013            return null;
     1014
     1015        int length = bytes.length;
     1016        for (int index = 0; index < bytes.length; index++) {
     1017            int i = bytes[index] & 0xFF;
     1018            if (i == 0 || i > 0x7F) {
     1019                length = index;
     1020                break;
     1021            }
     1022        }
     1023
     1024        return new String(bytes, 0, length);
     1025    }
     1026
     1027    @Nullable
     1028    public String getWhiteBalanceDescription()
     1029    {
     1030        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_WHITE_BALANCE);
     1031        if (value == null)
     1032            return null;
     1033        switch (value) {
     1034            case 1:
     1035                return "Auto";
     1036            case 2:
     1037                return "Daylight";
     1038            case 3:
     1039                return "Cloudy";
     1040            case 4:
     1041                return "Incandescent";
     1042            case 5:
     1043                return "Manual";
     1044            case 8:
     1045                return "Flash";
     1046            case 10:
     1047                return "Black & White";
     1048            case 11:
     1049                return "Manual";
     1050            case 12:
     1051                return "Shade";
     1052            default:
     1053                return "Unknown (" + value + ")";
     1054        }
     1055    }
     1056
     1057        @Nullable
     1058        public String getBabyAgeDescription()
     1059    {
     1060        final Age age = _directory.getAge(PanasonicMakernoteDirectory.TAG_BABY_AGE);
     1061        if (age==null)
     1062            return null;
     1063        return age.toFriendlyString();
     1064        }
     1065       
     1066        @Nullable
     1067        public String getBabyAge1Description()
     1068    {
     1069        final Age age = _directory.getAge(PanasonicMakernoteDirectory.TAG_BABY_AGE_1);
     1070        if (age==null)
     1071            return null;
     1072        return age.toFriendlyString();
     1073        }
     1074
     1075        @Nullable
     1076        private String getTextDescription(int tag)
     1077    {
     1078                byte[] values = _directory.getByteArray(tag);
     1079        if (values == null)
     1080            return null;
     1081        try {
     1082            return new String(values, "ASCII").trim();
     1083        } catch (UnsupportedEncodingException e) {
     1084            return null;
     1085        }
     1086        }
     1087
     1088    @Nullable
     1089    private String getOnOffDescription(int tag)
     1090    {
     1091        Integer value = _directory.getInteger(tag);
     1092        if (value == null)
     1093            return null;
     1094        switch (value) {
     1095            case 1:
     1096                return "Off";
     1097            case 2:
     1098                return "On";
    811099            default:
    821100                return "Unknown (" + value + ")";
  • trunk/src/com/drew/metadata/exif/PanasonicMakernoteDirectory.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.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.ByteArrayReader;
     26import com.drew.lang.annotations.NotNull;
     27import com.drew.lang.annotations.Nullable;
     28import com.drew.metadata.Age;
    1929import com.drew.metadata.Directory;
     30import com.drew.metadata.Face;
    2031
    2132import java.util.HashMap;
    2233
    2334/**
     35 * Describes tags specific to Panasonic and Leica cameras.
    2436 *
     37 * @author Drew Noakes http://drewnoakes.com, Philipp Sandhaus
    2538 */
    2639public class PanasonicMakernoteDirectory extends Directory
    2740{
    28     public static final int TAG_PANASONIC_QUALITY_MODE = 0x0001;
    29     public static final int TAG_PANASONIC_VERSION = 0x0002;
    30     /**
    31      * 1 = On
    32      * 2 = Off
    33      */
    34     public static final int TAG_PANASONIC_MACRO_MODE = 0x001C;
    35     /**
    36      * 1 = Normal
    37      * 2 = Portrait
    38      * 9 = Macro
    39      */
    40     public static final int TAG_PANASONIC_RECORD_MODE = 0x001F;
    41     public static final int TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    42 
    43     protected static final HashMap tagNameMap = new HashMap();
     41
     42    /**
     43     * <br>
     44     * 2 = High            <br>
     45     * 3 = Normal          <br>
     46     * 6 = Very High       <br>
     47     * 7 = Raw             <br>
     48     * 9 = Motion Picture  <br>
     49     */
     50    public static final int TAG_QUALITY_MODE = 0x0001;
     51    public static final int TAG_VERSION = 0x0002;
     52   
     53    /**                   
     54     * <br>
     55     * 1 = Auto            <br>
     56     * 2 = Daylight        <br>
     57     * 3 = Cloudy          <br>
     58     * 4 = Incandescent    <br>
     59     * 5 = Manual          <br>
     60     * 8 = Flash           <br>
     61     * 10 = Black & White  <br>
     62     * 11 = Manual         <br>
     63     * 12 = Shade          <br>
     64     */
     65    public static final int TAG_WHITE_BALANCE = 0x0003;
     66
     67
     68    /**                       
     69     * <br>
     70     * 1 = Auto                <br>
     71     * 2 = Manual              <br>
     72     * 4 =  Auto, Focus Button <br>
     73     * 5 = Auto, Continuous    <br>
     74     */
     75    public static final int TAG_FOCUS_MODE = 0x0007;
     76
     77    /**
     78     * <br>
     79     * 2 bytes                         <br>
     80     * (DMC-FZ10)                      <br>
     81     * '0 1' = Spot Mode On            <br>
     82     * '0 16' = Spot Mode Off          <br>
     83     * '(other models)                 <br>
     84     * 16 = Normal?                    <br>
     85     * '0 1' = 9-area                  <br>
     86     * '0 16' = 3-area (high speed)    <br>
     87     * '1 0' = Spot Focusing           <br>
     88     * '1 1' = 5-area                  <br>
     89     * '16 0' = 1-area                 <br>
     90     * '16 16' = 1-area (high speed)   <br>
     91     * '32 0' = Auto or Face Detect    <br>
     92     * '32 1' = 3-area (left)?         <br>
     93     * '32 2' = 3-area (center)?       <br>
     94     * '32 3' = 3-area (right)?        <br>
     95     * '64 0' = Face Detect            <br>
     96     */
     97    public static final int TAG_AF_AREA_MODE = 0x000f;
     98
     99    /**
     100     * <br>
     101     * 2 = On, Mode 1   <br>
     102     * 3 = Off          <br>
     103     * 4 = On, Mode 2   <br>
     104     */
     105    public static final int TAG_IMAGE_STABILIZATION = 0x001a;
     106
     107    /**
     108     * <br>
     109     * 1 = On    <br>
     110     * 2 = Off   <br>
     111     */
     112    public static final int TAG_MACRO_MODE = 0x001C;
     113
     114    /**
     115     * <br>
     116     * 1 = Normal                            <br>
     117     * 2 = Portrait                          <br>
     118     * 3 = Scenery                           <br>
     119     * 4 = Sports                            <br>
     120     * 5 = Night Portrait                    <br>
     121     * 6 = Program                           <br>
     122     * 7 = Aperture Priority                 <br>
     123     * 8 = Shutter Priority                  <br>
     124     * 9 = Macro                             <br>
     125     * 10= Spot                              <br>
     126     * 11= Manual                            <br>
     127     * 12= Movie Preview                     <br>
     128     * 13= Panning                           <br>
     129     * 14= Simple                            <br>
     130     * 15= Color Effects                     <br>
     131     * 16= Self Portrait                     <br>
     132     * 17= Economy                           <br>
     133     * 18= Fireworks                         <br>
     134     * 19= Party                             <br>
     135     * 20= Snow                              <br>
     136     * 21= Night Scenery                     <br>
     137     * 22= Food                              <br>
     138     * 23= Baby                              <br>
     139     * 24= Soft Skin                         <br>
     140     * 25= Candlelight                       <br>
     141     * 26= Starry Night                      <br>
     142     * 27= High Sensitivity                  <br>
     143     * 28= Panorama Assist                   <br>
     144     * 29= Underwater                        <br>
     145     * 30= Beach                             <br>
     146     * 31= Aerial Photo                      <br>
     147     * 32= Sunset                            <br>
     148     * 33= Pet                               <br>
     149     * 34= Intelligent ISO                   <br>
     150     * 35= Clipboard                         <br>
     151     * 36= High Speed Continuous Shooting    <br>
     152     * 37= Intelligent Auto                  <br>
     153     * 39= Multi-aspect                      <br>
     154     * 41= Transform                         <br>
     155     * 42= Flash Burst                       <br>
     156     * 43= Pin Hole                          <br>
     157     * 44= Film Grain                        <br>
     158     * 45= My Color                          <br>
     159     * 46= Photo Frame                       <br>
     160     * 51= HDR                               <br>
     161     */
     162    public static final int TAG_RECORD_MODE = 0x001F;
     163   
     164    /**
     165     * 1 = Yes <br>
     166     * 2 = No  <br>
     167     */
     168    public static final int TAG_AUDIO = 0x0020;
     169
     170    /**
     171     * No idea, what this is
     172     */
     173    public static final int TAG_UNKNOWN_DATA_DUMP = 0x0021;
     174   
     175    public static final int TAG_WHITE_BALANCE_BIAS = 0x0023;
     176    public static final int TAG_FLASH_BIAS = 0x0024;
     177   
     178    /**
     179     * this number is unique, and contains the date of manufacture,
     180     * but is not the same as the number printed on the camera body
     181     */
     182    public static final int TAG_INTERNAL_SERIAL_NUMBER = 0x0025;
     183
     184    /**
     185     * Panasonic Exif Version
     186     */
     187    public static final int TAG_EXIF_VERSION = 0x0026;
     188   
     189   
     190    /**
     191     * 1 = Off           <br>
     192     * 2 = Warm          <br>
     193     * 3 = Cool          <br>
     194     * 4 = Black & White <br>
     195     * 5 = Sepia         <br>
     196     */
     197    public static final int TAG_COLOR_EFFECT = 0x0028;
     198
     199    /**
     200     * 4 Bytes <br>
     201     * Time in 1/100 s from when the camera was powered on to when the
     202     * image is written to memory card
     203     */
     204    public static final int TAG_UPTIME = 0x0029;
     205
     206
     207    /**
     208     * 0 = Off        <br>
     209     * 1 = On         <br>
     210     * 2 = Infinite   <br>
     211     * 4 = Unlimited  <br>
     212     */
     213    public static final int TAG_BURST_MODE = 0x002a;
     214   
     215    public static final int TAG_SEQUENCE_NUMBER = 0x002b;
     216   
     217    /**
     218     * (this decoding seems to work for some models such as the LC1, LX2, FZ7, FZ8, FZ18 and FZ50, but may not be correct for other models such as the FX10, G1, L1, L10 and LC80) <br>
     219     * 0x0 = Normal                                            <br>
     220     * 0x1 = Low                                               <br>
     221     * 0x2 = High                                              <br>
     222     * 0x6 = Medium Low                                        <br>
     223     * 0x7 = Medium High                                       <br>
     224     * 0x100 = Low                                             <br>
     225     * 0x110 = Normal                                          <br>
     226     * 0x120 = High                                            <br>
     227     * (these values are used by the GF1)                      <br>
     228     * 0 = -2                                                  <br>
     229     * 1 = -1                                                  <br>
     230     * 2 = Normal                                              <br>
     231     * 3 = +1                                                  <br>
     232     * 4 = +2                                                  <br>
     233     * 7 = Nature (Color Film)                                 <br>
     234     * 12 = Smooth (Color Film) or Pure (My Color)             <br>
     235     * 17 = Dynamic (B&W Film)                                 <br>
     236     * 22 = Smooth (B&W Film)                                  <br>
     237     * 27 = Dynamic (Color Film)                               <br>
     238     * 32 = Vibrant (Color Film) or Expressive (My Color)      <br>
     239     * 33 = Elegant (My Color)                                 <br>
     240     * 37 = Nostalgic (Color Film)                             <br>
     241     * 41 = Dynamic Art (My Color)                             <br>
     242     * 42 = Retro (My Color)                                   <br>
     243     */
     244    public static final int TAG_CONTRAST_MODE = 0x002c;
     245   
     246   
     247    /**
     248     * 0 = Standard      <br>
     249     * 1 = Low (-1)      <br>
     250     * 2 = High (+1)     <br>
     251     * 3 = Lowest (-2)   <br>
     252     * 4 = Highest (+2)  <br>
     253     */
     254    public static final int TAG_NOISE_REDUCTION = 0x002d;
     255
     256    /**
     257     * 1 = Off   <br>
     258     * 2 = 10 s  <br>
     259     * 3 = 2 s   <br>
     260     */
     261    public static final int TAG_SELF_TIMER = 0x002e;
     262
     263    /**
     264     * 1 = 0 DG    <br>
     265     * 3 = 180 DG  <br>
     266     * 6 =  90 DG  <br>
     267     * 8 = 270 DG  <br>
     268     */
     269    public static final int TAG_ROTATION = 0x0030;
     270
     271    /**
     272     * 1 = Fired <br>
     273     * 2 = Enabled nut not used <br>
     274     * 3 = Disabled but required <br>
     275     * 4 = Disabled and not required
     276     */
     277    public static final int TAG_AF_ASSIST_LAMP = 0x0031;
     278   
     279    /**
     280     * 0 = Normal <br>
     281     * 1 = Natural<br>
     282     * 2 = Vivid
     283     *
     284     */
     285    public static final int TAG_COLOR_MODE = 0x0032;
     286   
     287    public static final int TAG_BABY_AGE = 0x0033;
     288   
     289    /**
     290     *  1 = Standard <br>
     291     *  2 = Extended
     292     */
     293    public static final int TAG_OPTICAL_ZOOM_MODE = 0x0034;
     294   
     295    /**
     296     * 1 = Off <br>
     297     * 2 = Wide <br>
     298     * 3 = Telephoto <br>
     299     * 4 = Macro
     300     */
     301    public static final int TAG_CONVERSION_LENS = 0x0035;
     302   
     303    public static final int TAG_TRAVEL_DAY = 0x0036;
     304   
     305    /**
     306     * 0 = Normal
     307     */
     308    public static final int TAG_CONTRAST = 0x0039;
     309   
     310    /**
     311     * <br>
     312     * 1 = Home <br>
     313     * 2 = Destination
     314     */
     315    public static final int TAG_WORLD_TIME_LOCATION = 0x003a;
     316   
     317    /**
     318     * 1 = Off   <br>
     319     * 2 = On
     320     */
     321    public static final int TAG_TEXT_STAMP = 0x003b;
     322
     323        public static final int TAG_PROGRAM_ISO = 0x003c;
     324   
     325    /**
     326     * <br>
     327     * 1 = Normal                               <br>
     328     * 2 = Outdoor/Illuminations/Flower/HDR Art <br>
     329     * 3 = Indoor/Architecture/Objects/HDR B&W  <br>
     330     * 4 = Creative                             <br>
     331     * 5 = Auto                                 <br>
     332     * 7 = Expressive                           <br>
     333     * 8 = Retro                                <br>
     334     * 9 = Pure                                 <br>
     335     * 10 = Elegant                             <br>
     336     * 12 = Monochrome                          <br>
     337     * 13 = Dynamic Art                         <br>
     338     * 14 = Silhouette                          <br>
     339     */
     340    public static final int TAG_ADVANCED_SCENE_MODE = 0x003d;
     341   
     342    /**
     343     * 1 = Off   <br>
     344     * 2 = On
     345     */
     346    public static final int TAG_TEXT_STAMP_1 = 0x003e;
     347       
     348    public static final int TAG_FACES_DETECTED = 0x003f;
     349
     350    public static final int TAG_SATURATION = 0x0040;
     351    public static final int TAG_SHARPNESS = 0x0041;
     352    public static final int TAG_FILM_MODE = 0x0042;
     353
     354    /**
     355         * WB adjust AB. Positive is a shift toward blue.
     356         */
     357        public static final int TAG_WB_ADJUST_AB = 0x0046;
     358    /**
     359         * WB adjust GM. Positive is a shift toward green.
     360         */
     361        public static final int TAG_WB_ADJUST_GM = 0x0047;
     362       
     363
     364    public static final int TAG_AF_POINT_POSITION = 0x004d;
     365   
     366   
     367    /**
     368     * <br>
     369     * Integer (16Bit) Indexes:                                             <br>
     370     * 0  Number Face Positions (maybe less than Faces Detected)            <br>
     371     * 1-4 Face Position 1                                                  <br>
     372     * 5-8 Face Position 2                                                  <br>
     373     * and so on                                                            <br>
     374     *                                                                      <br>
     375     * The four Integers are interpreted as follows:                        <br>
     376     * (XYWH)  X,Y Center of Face,  (W,H) Width and Height                  <br>
     377     * All values are in respect to double the size of the thumbnail image  <br>
     378     *
     379     */
     380    public static final int TAG_FACE_DETECTION_INFO = 0x004e;
     381    public static final int TAG_LENS_TYPE = 0x0051;
     382    public static final int TAG_LENS_SERIAL_NUMBER = 0x0052;
     383    public static final int TAG_ACCESSORY_TYPE = 0x0053;
     384   
     385    /**
     386     * (decoded as two 16-bit signed integers)
     387     * '-1 1' = Slim Low
     388     * '-3 2' = Slim High
     389     * '0 0' = Off
     390     * '1 1' = Stretch Low
     391     * '3 2' = Stretch High 
     392     */
     393    public static final int TAG_TRANSFORM = 0x0059;
     394   
     395    /**
     396    * 0 = Off <br>
     397    * 1 = Low <br>
     398    * 2 = Standard <br>
     399    * 3 = High
     400    */
     401    public static final int TAG_INTELLIGENT_EXPOSURE = 0x005d;
     402   
     403    /**
     404          * Info at http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
     405          *
     406     */
     407        public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
     408
     409
     410
     411    /**                                                                                   
     412     * Byte Indexes:                                                                       <br>
     413     *  0    Int (2  Byte) Number of Recognized Faces                                      <br>
     414     *  4    String(20 Byte)    Recognized Face 1 Name                                     <br>
     415     * 24    4 Int (8 Byte)     Recognized Face 1 Position  (Same Format as Face Detection)  <br>
     416     * 32    String(20 Byte)    Recognized Face 1 Age                                      <br>
     417     * 52    String(20 Byte)    Recognized Face 2 Name                                     <br>
     418     * 72    4 Int (8 Byte)     Recognized Face 2 Position  (Same Format as Face Detection)  <br>
     419     * 80    String(20 Byte)    Recognized Face 2 Age                                      <br>
     420     *                                                                                     <br>
     421     * And so on                                                                           <br>
     422     *                                                                                     <br>
     423     * The four Integers are interpreted as follows:                                       <br>
     424     * (XYWH)  X,Y Center of Face,  (W,H) Width and Height                                 <br>
     425     * All values are in respect to double the size of the thumbnail image                 <br>
     426     *
     427     */
     428    public static final int TAG_FACE_RECOGNITION_INFO = 0x0061;
     429
     430    /**
     431    * 0 = No <br>
     432    * 1 = Yes
     433    */
     434    public static final int TAG_FLASH_WARNING = 0x0062;
     435    public static final int TAG_RECOGNIZED_FACE_FLAGS = 0x0063;
     436    public static final int TAG_TITLE = 0x0065;
     437        public static final int TAG_BABY_NAME = 0x0066;
     438        public static final int TAG_LOCATION = 0x0067;
     439        public static final int TAG_COUNTRY = 0x0069;
     440    public static final int TAG_STATE = 0x006b;
     441    public static final int TAG_CITY = 0x006d;
     442    public static final int TAG_LANDMARK = 0x006f;
     443   
     444    /**
     445     * 0 = Off <br>
     446     * 2 = Auto <br>
     447     * 3 = On
     448     */
     449    public static final int TAG_INTELLIGENT_RESOLUTION = 0x0070;
     450   
     451    public static final int TAG_MAKERNOTE_VERSION = 0x8000;
     452    public static final int TAG_SCENE_MODE = 0x8001;
     453    public static final int TAG_WB_RED_LEVEL = 0x8004;
     454    public static final int TAG_WB_GREEN_LEVEL = 0x8005;
     455    public static final int TAG_WB_BLUE_LEVEL = 0x8006;
     456    public static final int TAG_FLASH_FIRED = 0x8007;
     457    public static final int TAG_TEXT_STAMP_2 = 0x8008;
     458        public static final int TAG_TEXT_STAMP_3 = 0x8009;
     459        public static final int TAG_BABY_AGE_1 = 0x8010;
     460       
     461        /**
     462     * (decoded as two 16-bit signed integers)
     463     * '-1 1' = Slim Low
     464     * '-3 2' = Slim High
     465     * '0 0' = Off
     466     * '1 1' = Stretch Low
     467     * '3 2' = Stretch High 
     468     */
     469    public static final int TAG_TRANSFORM_1 = 0x8012;
     470
     471    @NotNull
     472    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    44473
    45474    static
    46475    {
    47         tagNameMap.put(new Integer(TAG_PANASONIC_QUALITY_MODE), "Quality Mode");
    48         tagNameMap.put(new Integer(TAG_PANASONIC_VERSION), "Version");
    49         tagNameMap.put(new Integer(TAG_PANASONIC_MACRO_MODE), "Macro Mode");
    50         tagNameMap.put(new Integer(TAG_PANASONIC_RECORD_MODE), "Record Mode");
    51         tagNameMap.put(new Integer(TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");
     476        _tagNameMap.put(TAG_QUALITY_MODE, "Quality Mode");
     477        _tagNameMap.put(TAG_VERSION, "Version");
     478        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
     479        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
     480        _tagNameMap.put(TAG_AF_AREA_MODE, "AF Area Mode");
     481        _tagNameMap.put(TAG_IMAGE_STABILIZATION, "Image Stabilization");
     482        _tagNameMap.put(TAG_MACRO_MODE, "Macro Mode");
     483        _tagNameMap.put(TAG_RECORD_MODE, "Record Mode");
     484        _tagNameMap.put(TAG_AUDIO, "Audio");
     485        _tagNameMap.put(TAG_INTERNAL_SERIAL_NUMBER, "Internal Serial Number");
     486        _tagNameMap.put(TAG_UNKNOWN_DATA_DUMP, "Unknown Data Dump");
     487        _tagNameMap.put(TAG_WHITE_BALANCE_BIAS, "White Balance Bias");
     488        _tagNameMap.put(TAG_FLASH_BIAS, "Flash Bias");
     489        _tagNameMap.put(TAG_EXIF_VERSION, "Exif Version");
     490        _tagNameMap.put(TAG_COLOR_EFFECT, "Color Effect");
     491        _tagNameMap.put(TAG_UPTIME, "Camera Uptime");
     492        _tagNameMap.put(TAG_BURST_MODE, "Burst Mode");
     493        _tagNameMap.put(TAG_SEQUENCE_NUMBER, "Sequence Number");
     494        _tagNameMap.put(TAG_CONTRAST_MODE, "Contrast Mode");
     495        _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction");
     496        _tagNameMap.put(TAG_SELF_TIMER, "Self Timer");
     497        _tagNameMap.put(TAG_ROTATION, "Rotation");
     498        _tagNameMap.put(TAG_AF_ASSIST_LAMP, "AF Assist Lamp");
     499        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
     500        _tagNameMap.put(TAG_BABY_AGE, "Baby Age");
     501        _tagNameMap.put(TAG_OPTICAL_ZOOM_MODE, "Optical Zoom Mode");
     502        _tagNameMap.put(TAG_CONVERSION_LENS, "Conversion Lens");
     503        _tagNameMap.put(TAG_TRAVEL_DAY, "Travel Day");
     504        _tagNameMap.put(TAG_CONTRAST, "Contrast");
     505        _tagNameMap.put(TAG_WORLD_TIME_LOCATION, "World Time Location");
     506        _tagNameMap.put(TAG_TEXT_STAMP, "Text Stamp");
     507        _tagNameMap.put(TAG_PROGRAM_ISO, "Program ISO");
     508                _tagNameMap.put(TAG_ADVANCED_SCENE_MODE, "Advanced Scene Mode");
     509        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
     510        _tagNameMap.put(TAG_FACES_DETECTED, "Number of Detected Faces");
     511        _tagNameMap.put(TAG_SATURATION, "Saturation");
     512        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
     513        _tagNameMap.put(TAG_FILM_MODE, "Film Mode");
     514        _tagNameMap.put(TAG_WB_ADJUST_AB, "White Balance Adjust (AB)");
     515                _tagNameMap.put(TAG_WB_ADJUST_GM, "White Balance Adjust (GM)");
     516                _tagNameMap.put(TAG_AF_POINT_POSITION, "Af Point Position");
     517        _tagNameMap.put(TAG_FACE_DETECTION_INFO, "Face Detection Info");
     518        _tagNameMap.put(TAG_LENS_TYPE, "Lens Type");
     519        _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
     520        _tagNameMap.put(TAG_ACCESSORY_TYPE, "Accessory Type");
     521        _tagNameMap.put(TAG_TRANSFORM, "Transform");
     522        _tagNameMap.put(TAG_INTELLIGENT_EXPOSURE, "Intelligent Exposure");
     523        _tagNameMap.put(TAG_FACE_RECOGNITION_INFO, "Face Recognition Info");
     524        _tagNameMap.put(TAG_FLASH_WARNING, "Flash Warning");
     525        _tagNameMap.put(TAG_RECOGNIZED_FACE_FLAGS, "Recognized Face Flags");
     526                _tagNameMap.put(TAG_TITLE, "Title");
     527                _tagNameMap.put(TAG_BABY_NAME, "Baby Name");
     528                _tagNameMap.put(TAG_LOCATION, "Location");
     529                _tagNameMap.put(TAG_COUNTRY, "Country");
     530        _tagNameMap.put(TAG_STATE, "State");
     531        _tagNameMap.put(TAG_CITY, "City");
     532        _tagNameMap.put(TAG_LANDMARK, "Landmark");
     533        _tagNameMap.put(TAG_INTELLIGENT_RESOLUTION, "Intelligent Resolution");
     534        _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
     535        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
     536        _tagNameMap.put(TAG_WB_RED_LEVEL, "White Balance (Red)");
     537        _tagNameMap.put(TAG_WB_GREEN_LEVEL, "White Balance (Green)");
     538        _tagNameMap.put(TAG_WB_BLUE_LEVEL, "White Balance (Blue)");
     539        _tagNameMap.put(TAG_FLASH_FIRED, "Flash Fired");
     540                _tagNameMap.put(TAG_TEXT_STAMP_1, "Text Stamp 1");
     541                _tagNameMap.put(TAG_TEXT_STAMP_2, "Text Stamp 2");
     542                _tagNameMap.put(TAG_TEXT_STAMP_3, "Text Stamp 3");
     543                _tagNameMap.put(TAG_BABY_AGE_1, "Baby Age 1");
     544                _tagNameMap.put(TAG_TRANSFORM_1, "Transform 1");
    52545    }
    53546
     
    57550    }
    58551
     552    @NotNull
    59553    public String getName()
    60554    {
     
    62556    }
    63557
    64     protected HashMap getTagNameMap()
     558    @NotNull
     559    protected HashMap<Integer, String> getTagNameMap()
    65560    {
    66         return tagNameMap;
     561        return _tagNameMap;
    67562    }
     563
     564    @Nullable
     565    public Face[] getDetectedFaces()
     566    {
     567        byte[] bytes = getByteArray(PanasonicMakernoteDirectory.TAG_FACE_DETECTION_INFO);
     568        if (bytes==null)
     569            return null;
     570
     571        BufferReader reader = new ByteArrayReader(bytes);
     572        reader.setMotorolaByteOrder(false);
     573       
     574        try {
     575            int faceCount = reader.getUInt16(0);
     576            if (faceCount==0)
     577                return null;
     578            Face[] faces = new Face[faceCount];
     579
     580            for (int i = 0; i < faceCount; i++) {
     581                int offset = 2 + i * 8;
     582                faces[i] = new Face(
     583                        reader.getUInt16(offset),
     584                        reader.getUInt16(offset + 2),
     585                        reader.getUInt16(offset + 4),
     586                        reader.getUInt16(offset + 6)
     587                        , null, null);
     588            }
     589            return faces;
     590        } catch (BufferBoundsException e) {
     591            return null;
     592        }
     593    }
     594
     595    @Nullable
     596    public Face[] getRecognizedFaces()
     597    {
     598        byte[] bytes = getByteArray(PanasonicMakernoteDirectory.TAG_FACE_RECOGNITION_INFO);
     599        if (bytes == null)
     600            return null;
     601
     602        BufferReader reader = new ByteArrayReader(bytes);
     603        reader.setMotorolaByteOrder(false);
     604
     605        try {
     606            int faceCount = reader.getUInt16(0);
     607            if (faceCount==0)
     608                return null;
     609            Face[] faces = new Face[faceCount];
     610
     611            for (int i = 0; i < faceCount; i++) {
     612                int offset = 4 + i * 44;
     613                String name = reader.getString(offset, 20, "ASCII").trim();
     614                String age = reader.getString(offset + 28, 20, "ASCII").trim();
     615                faces[i] = new Face(
     616                        reader.getUInt16(offset + 20),
     617                        reader.getUInt16(offset + 22),
     618                        reader.getUInt16(offset + 24),
     619                        reader.getUInt16(offset + 26),
     620                        name,
     621                        Age.fromPanasonicString(age));
     622            }
     623            return faces;
     624        } catch (BufferBoundsException e) {
     625            return null;
     626        }
     627    }
     628
     629    /**
     630     * Attempts to convert the underlying string value (as stored in the directory) into an Age object.
     631     * @param tag The tag identifier.
     632     * @return The parsed Age object, or null if the tag was empty of the value unable to be parsed.
     633     */
     634        @Nullable
     635        public Age getAge(int tag)
     636    {
     637        final String ageString = getString(tag);
     638        if (ageString==null)
     639            return null;
     640        return Age.fromPanasonicString(ageString);
     641        }
    68642}
  • trunk/src/com/drew/metadata/exif/PentaxMakernoteDescriptor.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/
     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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
    2127/**
    22  * Provides human-readable string versions of the tags stored in PentaxMakernoteDirectory.
    23  *
     28 * Provides human-readable string representations of tag values stored in a <code>PentaxMakernoteDirectory</code>.
     29 * <p/>
    2430 * Some information about this makernote taken from here:
    2531 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/pentax_mn.html
     32 *
     33 * @author Drew Noakes http://drewnoakes.com
    2634 */
    27 public class PentaxMakernoteDescriptor extends TagDescriptor
     35public class PentaxMakernoteDescriptor extends TagDescriptor<PentaxMakernoteDirectory>
    2836{
    29     public PentaxMakernoteDescriptor(Directory directory)
     37    public PentaxMakernoteDescriptor(@NotNull PentaxMakernoteDirectory directory)
    3038    {
    3139        super(directory);
    3240    }
    3341
    34     public String getDescription(int tagType) throws MetadataException
     42    @Nullable
     43    public String getDescription(int tagType)
    3544    {
    3645        switch (tagType)
     
    5968                return getColourDescription();
    6069            default:
    61                 return _directory.getString(tagType);
    62         }
    63     }
    64 
    65     public String getColourDescription() throws MetadataException
    66     {
    67         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR)) return null;
    68         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR);
     70                return super.getDescription(tagType);
     71        }
     72    }
     73
     74    @Nullable
     75    public String getColourDescription()
     76    {
     77        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR);
     78        if (value==null)
     79            return null;
    6980        switch (value)
    7081        {
     
    7687    }
    7788
    78     public String getIsoSpeedDescription() throws MetadataException
    79     {
    80         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED)) return null;
    81         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED);
     89    @Nullable
     90    public String getIsoSpeedDescription()
     91    {
     92        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED);
     93        if (value==null)
     94            return null;
    8295        switch (value)
    8396        {
     
    91104    }
    92105
    93     public String getSaturationDescription() throws MetadataException
    94     {
    95         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION)) return null;
    96         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION);
     106    @Nullable
     107    public String getSaturationDescription()
     108    {
     109        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION);
     110        if (value==null)
     111            return null;
    97112        switch (value)
    98113        {
     
    104119    }
    105120
    106     public String getContrastDescription() throws MetadataException
    107     {
    108         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST)) return null;
    109         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST);
     121    @Nullable
     122    public String getContrastDescription()
     123    {
     124        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST);
     125        if (value==null)
     126            return null;
    110127        switch (value)
    111128        {
     
    117134    }
    118135
    119     public String getSharpnessDescription() throws MetadataException
    120     {
    121         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS)) return null;
    122         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS);
     136    @Nullable
     137    public String getSharpnessDescription()
     138    {
     139        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS);
     140        if (value==null)
     141            return null;
    123142        switch (value)
    124143        {
     
    130149    }
    131150
    132     public String getDigitalZoomDescription() throws MetadataException
    133     {
    134         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM)) return null;
    135         float value = _directory.getFloat(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM);
     151    @Nullable
     152    public String getDigitalZoomDescription()
     153    {
     154        Float value = _directory.getFloatObject(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM);
     155        if (value==null)
     156            return null;
    136157        if (value==0)
    137158            return "Off";
     
    139160    }
    140161
    141     public String getWhiteBalanceDescription() throws MetadataException
    142     {
    143         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE)) return null;
    144         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE);
     162    @Nullable
     163    public String getWhiteBalanceDescription()
     164    {
     165        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE);
     166        if (value==null)
     167            return null;
    145168        switch (value)
    146169        {
     
    155178    }
    156179
    157     public String getFlashModeDescription() throws MetadataException
    158     {
    159         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE)) return null;
    160         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE);
     180    @Nullable
     181    public String getFlashModeDescription()
     182    {
     183        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE);
     184        if (value==null)
     185            return null;
    161186        switch (value)
    162187        {
     
    169194    }
    170195
    171     public String getFocusModeDescription() throws MetadataException
    172     {
    173         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE)) return null;
    174         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE);
     196    @Nullable
     197    public String getFocusModeDescription()
     198    {
     199        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE);
     200        if (value==null)
     201            return null;
    175202        switch (value)
    176203        {
     
    181208    }
    182209
    183     public String getQualityLevelDescription() throws MetadataException
    184     {
    185         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL)) return null;
    186         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL);
     210    @Nullable
     211    public String getQualityLevelDescription()
     212    {
     213        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL);
     214        if (value==null)
     215            return null;
    187216        switch (value)
    188217        {
     
    194223    }
    195224
    196     public String getCaptureModeDescription() throws MetadataException
    197     {
    198         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE)) return null;
    199         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE);
     225    @Nullable
     226    public String getCaptureModeDescription()
     227    {
     228        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE);
     229        if (value==null)
     230            return null;
    200231        switch (value)
    201232        {
     
    209240
    210241/*
    211     public String getPrintImageMatchingInfoDescription() throws MetadataException
    212     {
    213         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null;
     242    public String getPrintImageMatchingInfoDescription()
     243    {
    214244        byte[] bytes = _directory.getByteArray(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO);
     245        if (bytes==null)
     246            return null;
    215247        return "(" + bytes.length + " bytes)";
    216248    }
    217249
    218     public String getMacroModeDescription() throws MetadataException
    219     {
    220         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null;
    221         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     250    public String getMacroModeDescription()
     251    {
     252        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     253        if (value==null)
     254            return null;
    222255        switch (value) {
    223256            case 1:
     
    230263    }
    231264
    232     public String getRecordModeDescription() throws MetadataException
    233     {
    234         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null;
    235         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     265    public String getRecordModeDescription()
     266    {
     267        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     268        if (value==null)
     269            return null;
    236270        switch (value) {
    237271            case 1:
  • trunk/src/com/drew/metadata/exif/PentaxMakernoteDirectory.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/**
    24  * Directory for metadata specific to Pentax and Asahi cameras.
     29 * Describes tags specific to Pentax and Asahi cameras.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class PentaxMakernoteDirectory extends Directory
     
    122129    public static final int TAG_PENTAX_DAYLIGHT_SAVINGS = 0x1001;
    123130
    124     protected static final HashMap tagNameMap = new HashMap();
     131    @NotNull
     132    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    125133
    126134    static
    127135    {
    128         tagNameMap.put(new Integer(TAG_PENTAX_CAPTURE_MODE), "Capture Mode");
    129         tagNameMap.put(new Integer(TAG_PENTAX_QUALITY_LEVEL), "Quality Level");
    130         tagNameMap.put(new Integer(TAG_PENTAX_FOCUS_MODE), "Focus Mode");
    131         tagNameMap.put(new Integer(TAG_PENTAX_FLASH_MODE), "Flash Mode");
    132         tagNameMap.put(new Integer(TAG_PENTAX_WHITE_BALANCE), "White Balance");
    133         tagNameMap.put(new Integer(TAG_PENTAX_DIGITAL_ZOOM), "Digital Zoom");
    134         tagNameMap.put(new Integer(TAG_PENTAX_SHARPNESS), "Sharpness");
    135         tagNameMap.put(new Integer(TAG_PENTAX_CONTRAST), "Contrast");
    136         tagNameMap.put(new Integer(TAG_PENTAX_SATURATION), "Saturation");
    137         tagNameMap.put(new Integer(TAG_PENTAX_ISO_SPEED), "ISO Speed");
    138         tagNameMap.put(new Integer(TAG_PENTAX_COLOUR), "Colour");
    139         tagNameMap.put(new Integer(TAG_PENTAX_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");
    140         tagNameMap.put(new Integer(TAG_PENTAX_TIME_ZONE), "Time Zone");
    141         tagNameMap.put(new Integer(TAG_PENTAX_DAYLIGHT_SAVINGS), "Daylight Savings");
     136        _tagNameMap.put(TAG_PENTAX_CAPTURE_MODE, "Capture Mode");
     137        _tagNameMap.put(TAG_PENTAX_QUALITY_LEVEL, "Quality Level");
     138        _tagNameMap.put(TAG_PENTAX_FOCUS_MODE, "Focus Mode");
     139        _tagNameMap.put(TAG_PENTAX_FLASH_MODE, "Flash Mode");
     140        _tagNameMap.put(TAG_PENTAX_WHITE_BALANCE, "White Balance");
     141        _tagNameMap.put(TAG_PENTAX_DIGITAL_ZOOM, "Digital Zoom");
     142        _tagNameMap.put(TAG_PENTAX_SHARPNESS, "Sharpness");
     143        _tagNameMap.put(TAG_PENTAX_CONTRAST, "Contrast");
     144        _tagNameMap.put(TAG_PENTAX_SATURATION, "Saturation");
     145        _tagNameMap.put(TAG_PENTAX_ISO_SPEED, "ISO Speed");
     146        _tagNameMap.put(TAG_PENTAX_COLOUR, "Colour");
     147        _tagNameMap.put(TAG_PENTAX_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
     148        _tagNameMap.put(TAG_PENTAX_TIME_ZONE, "Time Zone");
     149        _tagNameMap.put(TAG_PENTAX_DAYLIGHT_SAVINGS, "Daylight Savings");
    142150    }
    143151
     
    147155    }
    148156
     157    @NotNull
    149158    public String getName()
    150159    {
     
    152161    }
    153162
    154     protected HashMap getTagNameMap()
     163    @NotNull
     164    protected HashMap<Integer, String> getTagNameMap()
    155165    {
    156         return tagNameMap;
     166        return _tagNameMap;
    157167    }
    158168}
Note: See TracChangeset for help on using the changeset viewer.