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 }