Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/drew/metadata/exif/ExifDescriptor.java


1   /*
2    * Created by dnoakes on 12-Nov-2002 22:27:15 using IntelliJ IDEA.
3    */
4   package com.drew.metadata.exif;
5   
6   import com.drew.lang.Rational;
7   import com.drew.metadata.Directory;
8   import com.drew.metadata.MetadataException;
9   import com.drew.metadata.TagDescriptor;
10  
11  import java.text.DecimalFormat;
12  
13  /**
14   *
15   */
16  public class ExifDescriptor extends TagDescriptor
17  {
18      /**
19       * Dictates whether rational values will be represented in decimal format in instances
20       * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
21       */
22      private boolean _allowDecimalRepresentationOfRationals = true;
23  
24      public ExifDescriptor(Directory directory)
25      {
26          super(directory);
27      }
28  
29      /**
30       * Returns a descriptive value of the the specified tag for this image.
31       * Where possible, known values will be substituted here in place of the raw
32       * tokens actually kept in the Exif segment.  If no substitution is
33       * available, the value provided by getString(int) will be returned.
34       * @param tagType the tag to find a description for
35       * @return a description of the image's value for the specified tag, or
36       *         <code>null</code> if the tag hasn't been defined.
37       */
38      public String getDescription(int tagType) throws MetadataException
39      {
40          switch (tagType) {
41              case ExifDirectory.TAG_ORIENTATION:
42                  return getOrientationDescription();
43              case ExifDirectory.TAG_RESOLUTION_UNIT:
44                  return getResolutionDescription();
45              case ExifDirectory.TAG_YCBCR_POSITIONING:
46                  return getYCbCrPositioningDescription();
47              case ExifDirectory.TAG_EXPOSURE_TIME:
48                  return getExposureTimeDescription();
49              case ExifDirectory.TAG_SHUTTER_SPEED:
50                  return getShutterSpeedDescription();
51              case ExifDirectory.TAG_FNUMBER:
52                  return getFNumberDescription();
53              case ExifDirectory.TAG_X_RESOLUTION:
54                  return getXResolutionDescription();
55              case ExifDirectory.TAG_Y_RESOLUTION:
56                  return getYResolutionDescription();
57              case ExifDirectory.TAG_THUMBNAIL_OFFSET:
58                  return getThumbnailOffsetDescription();
59              case ExifDirectory.TAG_THUMBNAIL_LENGTH:
60                  return getThumbnailLengthDescription();
61              case ExifDirectory.TAG_COMPRESSION_LEVEL:
62                  return getCompressionLevelDescription();
63              case ExifDirectory.TAG_SUBJECT_DISTANCE:
64                  return getSubjectDistanceDescription();
65              case ExifDirectory.TAG_METERING_MODE:
66                  return getMeteringModeDescription();
67              case ExifDirectory.TAG_WHITE_BALANCE:
68                  return getWhiteBalanceDescription();
69              case ExifDirectory.TAG_FLASH:
70                  return getFlashDescription();
71              case ExifDirectory.TAG_FOCAL_LENGTH:
72                  return getFocalLengthDescription();
73              case ExifDirectory.TAG_COLOR_SPACE:
74                  return getColorSpaceDescription();
75              case ExifDirectory.TAG_EXIF_IMAGE_WIDTH:
76                  return getExifImageWidthDescription();
77              case ExifDirectory.TAG_EXIF_IMAGE_HEIGHT:
78                  return getExifImageHeightDescription();
79              case ExifDirectory.TAG_FOCAL_PLANE_UNIT:
80                  return getFocalPlaneResolutionUnitDescription();
81              case ExifDirectory.TAG_FOCAL_PLANE_X_RES:
82                  return getFocalPlaneXResolutionDescription();
83              case ExifDirectory.TAG_FOCAL_PLANE_Y_RES:
84                  return getFocalPlaneYResolutionDescription();
85              case ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH:
86                  return getThumbnailImageWidthDescription();
87              case ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT:
88                  return getThumbnailImageHeightDescription();
89              case ExifDirectory.TAG_BITS_PER_SAMPLE:
90                  return getBitsPerSampleDescription();
91              case ExifDirectory.TAG_COMPRESSION:
92                  return getCompressionDescription();
93              case ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION:
94                  return getPhotometricInterpretationDescription();
95              case ExifDirectory.TAG_ROWS_PER_STRIP:
96                  return getRowsPerStripDescription();
97              case ExifDirectory.TAG_STRIP_BYTE_COUNTS:
98                  return getStripByteCountsDescription();
99              case ExifDirectory.TAG_SAMPLES_PER_PIXEL:
100                 return getSamplesPerPixelDescription();
101             case ExifDirectory.TAG_PLANAR_CONFIGURATION:
102                 return getPlanarConfigurationDescription();
103             case ExifDirectory.TAG_YCBCR_SUBSAMPLING:
104                 return getYCbCrSubsamplingDescription();
105             case ExifDirectory.TAG_EXPOSURE_PROGRAM:
106                 return getExposureProgramDescription();
107             case ExifDirectory.TAG_APERTURE:
108                 return getApertureValueDescription();
109             case ExifDirectory.TAG_MAX_APERTURE:
110                 return getMaxApertureValueDescription();
111             case ExifDirectory.TAG_SENSING_METHOD:
112                 return getSensingMethodDescription();
113             case ExifDirectory.TAG_EXPOSURE_BIAS:
114                 return getExposureBiasDescription();
115             case ExifDirectory.TAG_FILE_SOURCE:
116                 return getFileSourceDescription();
117             case ExifDirectory.TAG_SCENE_TYPE:
118                 return getSceneTypeDescription();
119             case ExifDirectory.TAG_COMPONENTS_CONFIGURATION:
120                 return getComponentConfigurationDescription();
121             case ExifDirectory.TAG_EXIF_VERSION:
122                 return getExifVersionDescription();
123             case ExifDirectory.TAG_FLASHPIX_VERSION:
124                 return getFlashPixVersionDescription();
125             case ExifDirectory.TAG_REFERENCE_BLACK_WHITE:
126                 return getReferenceBlackWhiteDescription();
127             case ExifDirectory.TAG_ISO_EQUIVALENT:
128                 return getIsoEquivalentDescription();
129             case ExifDirectory.TAG_THUMBNAIL_DATA:
130                 return getThumbnailDescription();
131             default:
132                 return _directory.getString(tagType);
133         }
134     }
135 
136     private String getThumbnailDescription() throws MetadataException
137     {
138         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_DATA)) return null;
139         int[] thumbnailBytes = _directory.getIntArray(ExifDirectory.TAG_THUMBNAIL_DATA);
140         return "[" + thumbnailBytes.length + " bytes of thumbnail data]";
141     }
142 
143     private String getIsoEquivalentDescription() throws MetadataException
144     {
145         if (!_directory.containsTag(ExifDirectory.TAG_ISO_EQUIVALENT)) return null;
146         int isoEquiv = _directory.getInt(ExifDirectory.TAG_ISO_EQUIVALENT);
147         if (isoEquiv < 50) {
148             isoEquiv *= 200;
149         }
150         return Integer.toString(isoEquiv);
151     }
152 
153     private String getReferenceBlackWhiteDescription() throws MetadataException
154     {
155         if (!_directory.containsTag(ExifDirectory.TAG_REFERENCE_BLACK_WHITE)) return null;
156         int[] ints = _directory.getIntArray(ExifDirectory.TAG_REFERENCE_BLACK_WHITE);
157         int blackR = ints[0];
158         int whiteR = ints[1];
159         int blackG = ints[2];
160         int whiteG = ints[3];
161         int blackB = ints[4];
162         int whiteB = ints[5];
163         String pos = "[" + blackR + "," + blackG + "," + blackB + "] " +
164                 "[" + whiteR + "," + whiteG + "," + whiteB + "]";
165         return pos;
166     }
167 
168     private String getExifVersionDescription() throws MetadataException
169     {
170         if (!_directory.containsTag(ExifDirectory.TAG_EXIF_VERSION)) return null;
171         int[] ints = _directory.getIntArray(ExifDirectory.TAG_EXIF_VERSION);
172         return ExifDescriptor.convertBytesToVersionString(ints);
173     }
174 
175     private String getFlashPixVersionDescription() throws MetadataException
176     {
177         if (!_directory.containsTag(ExifDirectory.TAG_FLASHPIX_VERSION)) return null;
178         int[] ints = _directory.getIntArray(ExifDirectory.TAG_FLASHPIX_VERSION);
179         return ExifDescriptor.convertBytesToVersionString(ints);
180     }
181 
182     private String getSceneTypeDescription() throws MetadataException
183     {
184         if (!_directory.containsTag(ExifDirectory.TAG_SCENE_TYPE)) return null;
185         int sceneType = _directory.getInt(ExifDirectory.TAG_SCENE_TYPE);
186         if (sceneType == 1) {
187             return "Directly photographed image";
188         } else {
189             return "Unknown (" + sceneType + ")";
190         }
191     }
192 
193     private String getFileSourceDescription() throws MetadataException
194     {
195         if (!_directory.containsTag(ExifDirectory.TAG_FILE_SOURCE)) return null;
196         int fileSource = _directory.getInt(ExifDirectory.TAG_FILE_SOURCE);
197         if (fileSource == 3) {
198             return "Digital Still Camera (DSC)";
199         } else {
200             return "Unknown (" + fileSource + ")";
201         }
202     }
203 
204     private String getExposureBiasDescription() throws MetadataException
205     {
206         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_BIAS)) return null;
207         Rational exposureBias = _directory.getRational(ExifDirectory.TAG_EXPOSURE_BIAS);
208         return exposureBias.toSimpleString(true);
209     }
210 
211     private String getMaxApertureValueDescription() throws MetadataException
212     {
213         if (!_directory.containsTag(ExifDirectory.TAG_MAX_APERTURE)) return null;
214         double apertureApex = _directory.getDouble(ExifDirectory.TAG_MAX_APERTURE);
215         double rootTwo = Math.sqrt(2);
216         double fStop = Math.pow(rootTwo, apertureApex);
217         java.text.DecimalFormat formatter = new DecimalFormat("0.#");
218         return "F" + formatter.format(fStop);
219     }
220 
221     private String getApertureValueDescription() throws MetadataException
222     {
223         if (!_directory.containsTag(ExifDirectory.TAG_APERTURE)) return null;
224         double apertureApex = _directory.getDouble(ExifDirectory.TAG_APERTURE);
225         double rootTwo = Math.sqrt(2);
226         double fStop = Math.pow(rootTwo, apertureApex);
227         java.text.DecimalFormat formatter = new DecimalFormat("0.#");
228         return "F" + formatter.format(fStop);
229     }
230 
231     private String getExposureProgramDescription() throws MetadataException
232     {
233         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_PROGRAM)) return null;
234         // '1' means manual control, '2' program normal, '3' aperture priority,
235         // '4' shutter priority, '5' program creative (slow program),
236         // '6' program action(high-speed program), '7' portrait mode, '8' landscape mode.
237         switch (_directory.getInt(ExifDirectory.TAG_EXPOSURE_PROGRAM)) {
238             case 1:
239                 return "Manual control";
240             case 2:
241                 return "Program normal";
242             case 3:
243                 return "Aperture priority";
244             case 4:
245                 return "Shutter priority";
246             case 5:
247                 return "Program creative (slow program)";
248             case 6:
249                 return "Program action (high-speed program)";
250             case 7:
251                 return "Portrait mode";
252             case 8:
253                 return "Landscape mode";
254             default:
255                 return "Unknown program (" + _directory.getInt(ExifDirectory.TAG_EXPOSURE_PROGRAM) + ")";
256         }
257     }
258 
259     private String getYCbCrSubsamplingDescription() throws MetadataException
260     {
261         if (!_directory.containsTag(ExifDirectory.TAG_YCBCR_SUBSAMPLING)) return null;
262         int[] positions = _directory.getIntArray(ExifDirectory.TAG_YCBCR_SUBSAMPLING);
263         if (positions[0] == 2 && positions[1] == 1) {
264             return "YCbCr4:2:2";
265         } else if (positions[0] == 2 && positions[1] == 2) {
266             return "YCbCr4:2:0";
267         } else {
268             return "(Unknown)";
269         }
270     }
271 
272     private String getPlanarConfigurationDescription() throws MetadataException
273     {
274         if (!_directory.containsTag(ExifDirectory.TAG_PLANAR_CONFIGURATION)) return null;
275         // When image format is no compression YCbCr, this value shows byte aligns of YCbCr
276         // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling
277         // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr
278         // plane format.
279 
280         switch (_directory.getInt(ExifDirectory.TAG_PLANAR_CONFIGURATION)) {
281             case 1:
282                 return "Chunky (contiguous for each subsampling pixel)";
283             case 2:
284                 return "Separate (Y-plane/Cb-plane/Cr-plane format)";
285             default:
286                 return "Unknown configuration";
287         }
288     }
289 
290     private String getSamplesPerPixelDescription()
291     {
292         if (!_directory.containsTag(ExifDirectory.TAG_SAMPLES_PER_PIXEL)) return null;
293         return _directory.getString(ExifDirectory.TAG_SAMPLES_PER_PIXEL) + " samples/pixel";
294     }
295 
296     private String getRowsPerStripDescription()
297     {
298         if (!_directory.containsTag(ExifDirectory.TAG_ROWS_PER_STRIP)) return null;
299         return _directory.getString(ExifDirectory.TAG_ROWS_PER_STRIP) + " rows/strip";
300     }
301 
302     private String getStripByteCountsDescription()
303     {
304         if (!_directory.containsTag(ExifDirectory.TAG_STRIP_BYTE_COUNTS)) return null;
305         return _directory.getString(ExifDirectory.TAG_STRIP_BYTE_COUNTS) + " bytes";
306     }
307 
308     private String getPhotometricInterpretationDescription() throws MetadataException
309     {
310         if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION)) return null;
311         // Shows the color space of the image data components. '1' means monochrome,
312         // '2' means RGB, '6' means YCbCr.
313         switch (_directory.getInt(ExifDirectory.TAG_COMPRESSION)) {
314             case 1:
315                 return "Monochrome";
316             case 2:
317                 return "RGB";
318             case 6:
319                 return "YCbCr";
320             default:
321                 return "Unknown colour space";
322         }
323     }
324 
325     private String getCompressionDescription() throws MetadataException
326     {
327         if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION)) return null;
328         // '1' means no compression, '6' means JPEG compression.
329         switch (_directory.getInt(ExifDirectory.TAG_COMPRESSION)) {
330             case 1:
331                 return "No compression";
332             case 6:
333                 return "JPEG compression";
334             default:
335                 return "Unknown compression";
336         }
337     }
338 
339     private String getBitsPerSampleDescription()
340     {
341         if (!_directory.containsTag(ExifDirectory.TAG_BITS_PER_SAMPLE)) return null;
342         return _directory.getString(ExifDirectory.TAG_BITS_PER_SAMPLE) + " bits/component/pixel";
343     }
344 
345     private String getThumbnailImageWidthDescription()
346     {
347         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH)) return null;
348         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH) + " pixels";
349     }
350 
351     private String getThumbnailImageHeightDescription()
352     {
353         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT)) return null;
354         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT) + " pixels";
355     }
356 
357     private String getFocalPlaneXResolutionDescription() throws MetadataException
358     {
359         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_X_RES)) return null;
360         Rational rational = _directory.getRational(ExifDirectory.TAG_FOCAL_PLANE_X_RES);
361         return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +
362                 getFocalPlaneResolutionUnitDescription().toLowerCase();
363     }
364 
365     private String getFocalPlaneYResolutionDescription() throws MetadataException
366     {
367         if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION)) return null;
368         Rational rational = _directory.getRational(ExifDirectory.TAG_FOCAL_PLANE_Y_RES);
369         return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +
370                 getFocalPlaneResolutionUnitDescription().toLowerCase();
371     }
372 
373     private String getFocalPlaneResolutionUnitDescription() throws MetadataException
374     {
375         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_UNIT)) return null;
376         // Unit of FocalPlaneXResoluton/FocalPlaneYResolution. '1' means no-unit,
377         // '2' inch, '3' centimeter.
378         switch (_directory.getInt(ExifDirectory.TAG_FOCAL_PLANE_UNIT)) {
379             case 1:
380                 return "(No unit)";
381             case 2:
382                 return "Inches";
383             case 3:
384                 return "cm";
385             default:
386                 return "";
387         }
388     }
389 
390     private String getExifImageWidthDescription() throws MetadataException
391     {
392         if (!_directory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH)) return null;
393         return _directory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) + " pixels";
394     }
395 
396     private String getExifImageHeightDescription() throws MetadataException
397     {
398         if (!_directory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) return null;
399         return _directory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT) + " pixels";
400     }
401 
402     private String getColorSpaceDescription() throws MetadataException
403     {
404         if (!_directory.containsTag(ExifDirectory.TAG_COLOR_SPACE)) return null;
405         int colorSpace = _directory.getInt(ExifDirectory.TAG_COLOR_SPACE);
406         if (colorSpace == 1) {
407             return "sRGB";
408         } else if (colorSpace == 65535) {
409             return "Undefined";
410         } else {
411             return "Unknown";
412         }
413     }
414 
415     private String getFocalLengthDescription() throws MetadataException
416     {
417         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_LENGTH)) return null;
418         java.text.DecimalFormat formatter = new DecimalFormat("0.0##");
419         Rational focalLength = _directory.getRational(ExifDirectory.TAG_FOCAL_LENGTH);
420         return formatter.format(focalLength.doubleValue()) + " mm";
421     }
422 
423     private String getFlashDescription() throws MetadataException
424     {
425         if (!_directory.containsTag(ExifDirectory.TAG_FLASH)) return null;
426         // '0' means flash did not fire, '1' flash fired, '5' flash fired but strobe return
427         // light not detected, '7' flash fired and strobe return light detected.
428         switch (_directory.getInt(ExifDirectory.TAG_FLASH)) {
429             case 0:
430                 return "No flash fired";
431             case 1:
432                 return "Flash fired";
433             case 5:
434                 return "Flash fired but strobe return light not detected";
435             case 7:
436                 return "flash fired and strobe return light detected";
437             default:
438                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_FLASH) + ")";
439         }
440     }
441 
442     private String getWhiteBalanceDescription() throws MetadataException
443     {
444         if (!_directory.containsTag(ExifDirectory.TAG_WHITE_BALANCE)) return null;
445         // '0' means unknown, '1' daylight, '2' fluorescent, '3' tungsten, '10' flash,
446         // '17' standard light A, '18' standard light B, '19' standard light C, '20' D55,
447         // '21' D65, '22' D75, '255' other.
448         switch (_directory.getInt(ExifDirectory.TAG_WHITE_BALANCE)) {
449             case 0:
450                 return "Unknown";
451             case 1:
452                 return "Daylight";
453             case 2:
454                 return "Flourescent";
455             case 3:
456                 return "Tungsten";
457             case 10:
458                 return "Flash";
459             case 17:
460                 return "Standard light";
461             case 18:
462                 return "Standard light (B)";
463             case 19:
464                 return "Standard light (C)";
465             case 20:
466                 return "D55";
467             case 21:
468                 return "D65";
469             case 22:
470                 return "D75";
471             case 255:
472                 return "(Other)";
473             default:
474                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_WHITE_BALANCE) + ")";
475         }
476     }
477 
478     private String getMeteringModeDescription() throws MetadataException
479     {
480         if (!_directory.containsTag(ExifDirectory.TAG_METERING_MODE)) return null;
481         // '0' means unknown, '1' average, '2' center weighted average, '3' spot
482         // '4' multi-spot, '5' multi-segment, '6' partial, '255' other
483         int meteringMode = _directory.getInt(ExifDirectory.TAG_METERING_MODE);
484         switch (meteringMode) {
485             case 0:
486                 return "Unknown";
487             case 1:
488                 return "Average";
489             case 2:
490                 return "Center weighted average";
491             case 3:
492                 return "Spot";
493             case 4:
494                 return "Multi-spot";
495             case 5:
496                 return "Multi-segment";
497             case 6:
498                 return "Partial";
499             case 255:
500                 return "(Other)";
501             default:
502                 return "";
503         }
504     }
505 
506     private String getSubjectDistanceDescription() throws MetadataException
507     {
508         if (!_directory.containsTag(ExifDirectory.TAG_SUBJECT_DISTANCE)) return null;
509         Rational distance = _directory.getRational(ExifDirectory.TAG_SUBJECT_DISTANCE);
510         java.text.DecimalFormat formatter = new DecimalFormat("0.0##");
511         return formatter.format(distance.doubleValue()) + " metres";
512     }
513 
514     private String getCompressionLevelDescription() throws MetadataException
515     {
516         if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION_LEVEL)) return null;
517         Rational compressionRatio = _directory.getRational(ExifDirectory.TAG_COMPRESSION_LEVEL);
518         String ratio = compressionRatio.toSimpleString(_allowDecimalRepresentationOfRationals);
519         if (compressionRatio.isInteger() && compressionRatio.intValue() == 1) {
520             return ratio + " bit/pixel";
521         } else {
522             return ratio + " bits/pixel";
523         }
524     }
525 
526     private String getThumbnailLengthDescription()
527     {
528         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH)) return null;
529         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_LENGTH) + " bytes";
530     }
531 
532     private String getThumbnailOffsetDescription()
533     {
534         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET)) return null;
535         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_OFFSET) + " bytes";
536     }
537 
538     private String getYResolutionDescription() throws MetadataException
539     {
540         if (!_directory.containsTag(ExifDirectory.TAG_Y_RESOLUTION)) return null;
541         Rational inverseResolution = _directory.getRational(ExifDirectory.TAG_Y_RESOLUTION);
542         return inverseResolution.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +
543                 getResolutionDescription().toLowerCase();
544     }
545 
546     private String getXResolutionDescription() throws MetadataException
547     {
548         if (!_directory.containsTag(ExifDirectory.TAG_X_RESOLUTION)) return null;
549         Rational inverseResolution = _directory.getRational(ExifDirectory.TAG_X_RESOLUTION);
550         return inverseResolution.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +
551                 getResolutionDescription().toLowerCase();
552     }
553 
554     private String getExposureTimeDescription()
555     {
556         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_TIME)) return null;
557         return _directory.getString(ExifDirectory.TAG_EXPOSURE_TIME) + " sec";
558     }
559 
560     private String getShutterSpeedDescription() throws MetadataException
561     {
562         if (!_directory.containsTag(ExifDirectory.TAG_SHUTTER_SPEED)) return null;
563         // Incorrect math bug fixed by Hendrik Wördehoff - 20 Sep 2002
564         int apexValue = _directory.getInt(ExifDirectory.TAG_SHUTTER_SPEED);
565         int apexPower = (int)(Math.pow(2.0, apexValue) + 0.5);
566         return "1/" + apexPower + " sec";
567     }
568 
569     private String getFNumberDescription() throws MetadataException
570     {
571         if (!_directory.containsTag(ExifDirectory.TAG_FNUMBER)) return null;
572         Rational fNumber = _directory.getRational(ExifDirectory.TAG_FNUMBER);
573         java.text.DecimalFormat formatter = new DecimalFormat("0.#");
574         return "F" + formatter.format(fNumber.doubleValue());
575     }
576 
577     private String getYCbCrPositioningDescription() throws MetadataException
578     {
579         if (!_directory.containsTag(ExifDirectory.TAG_YCBCR_POSITIONING)) return null;
580         int yCbCrPosition = _directory.getInt(ExifDirectory.TAG_YCBCR_POSITIONING);
581         switch (yCbCrPosition) {
582             case 1:
583                 return "Center of pixel array";
584             case 2:
585                 return "Datum point";
586             default:
587                 return String.valueOf(yCbCrPosition);
588         }
589     }
590 
591     private String getOrientationDescription() throws MetadataException
592     {
593         if (!_directory.containsTag(ExifDirectory.TAG_ORIENTATION)) return null;
594         int orientation = _directory.getInt(ExifDirectory.TAG_ORIENTATION);
595         switch (orientation) {
596             case 1:
597                 return "top, left side";
598             case 2:
599                 return "top, right side";
600             case 3:
601                 return "bottom, right side";
602             case 4:
603                 return "bottom, left side";
604             case 5:
605                 return "left side, top";
606             case 6:
607                 return "right side, top";
608             case 7:
609                 return "right side, bottom";
610             case 8:
611                 return "left side, bottom";
612             default:
613                 return String.valueOf(orientation);
614         }
615     }
616 
617     private String getResolutionDescription() throws MetadataException
618     {
619         if (!_directory.containsTag(ExifDirectory.TAG_RESOLUTION_UNIT)) return "";
620         // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
621         int resolutionUnit = _directory.getInt(ExifDirectory.TAG_RESOLUTION_UNIT);
622         switch (resolutionUnit) {
623             case 1:
624                 return "(No unit)";
625             case 2:
626                 return "Inches";
627             case 3:
628                 return "cm";
629             default:
630                 return "";
631         }
632     }
633 
634     private String getSensingMethodDescription() throws MetadataException
635     {
636         if (!_directory.containsTag(ExifDirectory.TAG_SENSING_METHOD)) return null;
637         // '1' Not defined, '2' One-chip color area sensor, '3' Two-chip color area sensor
638         // '4' Three-chip color area sensor, '5' Color sequential area sensor
639         // '7' Trilinear sensor '8' Color sequential linear sensor,  'Other' reserved
640         int sensingMethod = _directory.getInt(ExifDirectory.TAG_SENSING_METHOD);
641         switch (sensingMethod) {
642             case 1:
643                 return "(Not defined)";
644             case 2:
645                 return "One-chip color area sensor";
646             case 3:
647                 return "Two-chip color area sensor";
648             case 4:
649                 return "Three-chip color area sensor";
650             case 5:
651                 return "Color sequential area sensor";
652             case 7:
653                 return "Trilinear sensor";
654             case 8:
655                 return "Color sequential linear sensor";
656             default:
657                 return "";
658         }
659     }
660 
661     private String getComponentConfigurationDescription() throws MetadataException
662     {
663         int[] components = _directory.getIntArray(ExifDirectory.TAG_COMPONENTS_CONFIGURATION);
664         String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"};
665         StringBuffer componentConfig = new StringBuffer();
666         for (int i = 0; i < Math.min(4, components.length); i++) {
667             int j = components[i];
668             if (j > 0 && j < componentStrings.length) {
669                 componentConfig.append(componentStrings[j]);
670             }
671         }
672         return componentConfig.toString();
673     }
674 
675     /**
676      * Takes a series of 4 bytes from the specified offset, and converts these to a
677      * well-known version number, where possible.  For example, (hex) 30 32 31 30 == 2.10).
678      * @param components the four version values
679      * @return the version as a string of form 2.10
680      */
681     public static String convertBytesToVersionString(int[] components)
682     {
683         StringBuffer version = new StringBuffer();
684         for (int i = 0; i < 4; i++) {
685             if (i == 2) version.append('.');
686             String digit = String.valueOf((char)components[i]);
687             if (i == 0 && "0".equals(digit)) continue;
688             version.append(digit);
689         }
690         return version.toString();
691     }
692 }