Changeset 15217 in josm
- Timestamp:
- 2019-07-07T01:56:46+02:00 (5 years ago)
- Location:
- trunk/src/com/drew
- Files:
-
- 3 added
- 3 deleted
- 120 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/imaging/ImageProcessingException.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/PhotographicConversions.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 35 35 //import com.drew.metadata.adobe.AdobeJpegReader; 36 36 import com.drew.metadata.exif.ExifReader; 37 import com.drew.metadata.file.File MetadataReader;37 import com.drew.metadata.file.FileSystemMetadataReader; 38 38 //import com.drew.metadata.icc.IccReader; 39 39 import com.drew.metadata.iptc.IptcReader; … … 95 95 inputStream.close(); 96 96 } 97 new File MetadataReader().read(file, metadata);97 new FileSystemMetadataReader().read(file, metadata); 98 98 return metadata; 99 99 } -
trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 153 153 segmentData.addSegment(segmentType, segmentBytes); 154 154 } else { 155 // S ome if the JPEG is truncated, just return what data we've already gathered155 // Skip this segment 156 156 if (!reader.trySkip(segmentLength)) { 157 // If skipping failed, just return the segments we found so far 157 158 return segmentData; 158 159 } -
trunk/src/com/drew/imaging/jpeg/JpegSegmentType.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffDataFormat.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffHandler.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffProcessingException.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/BufferBoundsException.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/ByteArrayReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/Charsets.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 38 38 public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); 39 39 public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); 40 public static final Charset WINDOWS_1252 = Charset.forName("Cp1252"); 40 41 } -
trunk/src/com/drew/lang/CompoundException.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/GeoLocation.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/RandomAccessReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 22 22 package com.drew.lang; 23 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.metadata.StringValue; 27 24 28 import java.io.IOException; 25 29 import java.io.UnsupportedEncodingException; 26 30 import java.nio.charset.Charset; 27 28 import com.drew.lang.annotations.NotNull;29 import com.drew.lang.annotations.Nullable;30 import com.drew.metadata.StringValue;31 31 32 32 /** -
trunk/src/com/drew/lang/Rational.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/SequentialByteArrayReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/SequentialReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/StreamReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/StringUtil.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/annotations/NotNull.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/annotations/Nullable.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Age.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Directory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 757 757 if (o == null) 758 758 return null; 759 if (o instanceof Number) 760 return ((Number)o).longValue(); 759 761 if (o instanceof String || o instanceof StringValue) { 760 762 try { … … 763 765 return null; 764 766 } 765 } 766 if (o instanceof Number) 767 return ((Number)o).longValue(); 767 } else if (o instanceof Rational[]) { 768 Rational[] rationals = (Rational[])o; 769 if (rationals.length == 1) 770 return rationals[0].longValue(); 771 } else if (o instanceof byte[]) { 772 byte[] bytes = (byte[])o; 773 if (bytes.length == 1) 774 return (long)bytes[0]; 775 } else if (o instanceof int[]) { 776 int[] ints = (int[])o; 777 if (ints.length == 1) 778 return (long)ints[0]; 779 } else if (o instanceof short[]) { 780 short[] shorts = (short[])o; 781 if (shorts.length == 1) 782 return (long)shorts[0]; 783 } 768 784 return null; 769 785 } … … 1009 1025 } 1010 1026 } else if (componentType.getName().equals("float")) { 1027 DecimalFormat format = new DecimalFormat(_floatFormatPattern); 1011 1028 for (int i = 0; i < arrayLength; i++) { 1012 1029 if (i != 0) 1013 1030 string.append(' '); 1014 string.append(new DecimalFormat(_floatFormatPattern).format(Array.getFloat(o, i))); 1031 String s = format.format(Array.getFloat(o, i)); 1032 string.append(s.equals("-0") ? "0" : s); 1015 1033 } 1016 1034 } else if (componentType.getName().equals("double")) { 1035 DecimalFormat format = new DecimalFormat(_floatFormatPattern); 1017 1036 for (int i = 0; i < arrayLength; i++) { 1018 1037 if (i != 0) 1019 1038 string.append(' '); 1020 string.append(new DecimalFormat(_floatFormatPattern).format(Array.getDouble(o, i))); 1039 String s = format.format(Array.getDouble(o, i)); 1040 string.append(s.equals("-0") ? "0" : s); 1021 1041 } 1022 1042 } else if (componentType.getName().equals("byte")) { -
trunk/src/com/drew/metadata/ErrorDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Face.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Metadata.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/MetadataException.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/StringValue.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Tag.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/TagDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 34 34 import java.util.ArrayList; 35 35 import java.util.Date; 36 import java.util.HashMap; 36 37 import java.util.List; 38 import java.util.Map; 37 39 38 40 /** … … 79 81 } 80 82 81 if (object instanceof Date) 82 { 83 if (object instanceof Date) { 83 84 // Produce a date string having a format that includes the offset in form "+00:00" 84 85 return new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy") … … 140 141 protected String getIndexedDescription(final int tagType, final int baseIndex, @NotNull String... descriptions) 141 142 { 142 final Integer index = _directory.getInteger(tagType);143 final Long index = _directory.getLongObject(tagType); 143 144 if (index == null) 144 145 return null; 145 final intarrayIndex = index - baseIndex;146 final long arrayIndex = index - baseIndex; 146 147 if (arrayIndex >= 0 && arrayIndex < descriptions.length) { 147 String description = descriptions[ arrayIndex];148 String description = descriptions[(int)arrayIndex]; 148 149 if (description != null) 149 150 return description; … … 211 212 // TODO have observed a byte[8] here which is likely some kind of date (ticks as long?) 212 213 Long value = _directory.getLongObject(tagType); 213 if (value ==null)214 if (value == null) 214 215 return null; 215 216 return new Date(value).toString(); … … 227 228 return null; 228 229 229 List<String> parts = new ArrayList< String>();230 List<String> parts = new ArrayList<>(); 230 231 231 232 int bitIndex = 0; … … 292 293 293 294 Double d = _directory.getDoubleObject(tagType); 294 if (d != null) 295 { 295 if (d != null) { 296 296 DecimalFormat format = new DecimalFormat("0.###"); 297 297 return format.format(d); … … 379 379 if (apexValue <= 1) { 380 380 float apexPower = (float)(1 / (Math.exp(apexValue * Math.log(2)))); 381 long apexPower10 = Math.round( (double)apexPower * 10.0);382 float fApexPower = (float)apexPower10 / 10.0f;381 long apexPower10 = Math.round(apexPower * 10.0); 382 float fApexPower = apexPower10 / 10.0f; 383 383 DecimalFormat format = new DecimalFormat("0.##"); 384 384 format.setRoundingMode(RoundingMode.HALF_UP); … … 414 414 protected String getLightSourceDescription(short wbtype) 415 415 { 416 switch (wbtype) 417 { 416 switch (wbtype) { 418 417 case 0: 419 418 return "Unknown"; … … 464 463 return getDescription(wbtype); 465 464 } 465 466 // EXIF UserComment, GPSProcessingMethod and GPSAreaInformation 467 @Nullable 468 protected String getEncodedTextDescription(int tagType) 469 { 470 byte[] commentBytes = _directory.getByteArray(tagType); 471 if (commentBytes == null) 472 return null; 473 if (commentBytes.length == 0) 474 return ""; 475 476 final Map<String, String> encodingMap = new HashMap<>(); 477 encodingMap.put("ASCII", System.getProperty("file.encoding")); // Someone suggested "ISO-8859-1". 478 encodingMap.put("UNICODE", "UTF-16LE"); 479 encodingMap.put("JIS", "Shift-JIS"); // We assume this charset for now. Another suggestion is "JIS". 480 481 try { 482 if (commentBytes.length >= 10) { 483 String firstTenBytesString = new String(commentBytes, 0, 10); 484 485 // try each encoding name 486 for (Map.Entry<String, String> pair : encodingMap.entrySet()) { 487 String encodingName = pair.getKey(); 488 String charset = pair.getValue(); 489 if (firstTenBytesString.startsWith(encodingName)) { 490 // skip any null or blank characters commonly present after the encoding name, up to a limit of 10 from the start 491 for (int j = encodingName.length(); j < 10; j++) { 492 byte b = commentBytes[j]; 493 if (b != '\0' && b != ' ') 494 return new String(commentBytes, j, commentBytes.length - j, charset).trim(); 495 } 496 return new String(commentBytes, 10, commentBytes.length - 10, charset).trim(); 497 } 498 } 499 } 500 // special handling fell through, return a plain string representation 501 return new String(commentBytes, System.getProperty("file.encoding")).trim(); 502 } catch (UnsupportedEncodingException ex) { 503 return null; 504 } 505 } 466 506 } -
trunk/src/com/drew/metadata/exif/ExifDescriptorBase.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 33 33 import java.io.UnsupportedEncodingException; 34 34 import java.text.DecimalFormat; 35 import java.util.HashMap;36 import java.util.Map;37 35 38 36 import static com.drew.metadata.exif.ExifDirectoryBase.*; … … 67 65 public String getDescription(int tagType) 68 66 { 69 // TODO order case blocks and corresponding methods in the same order as the TAG_* values are defined70 71 67 switch (tagType) { 72 68 case TAG_INTEROP_INDEX: … … 74 70 case TAG_INTEROP_VERSION: 75 71 return getInteropVersionDescription(); 76 case TAG_ORIENTATION: 77 return getOrientationDescription(); 78 case TAG_RESOLUTION_UNIT: 79 return getResolutionDescription(); 80 case TAG_YCBCR_POSITIONING: 81 return getYCbCrPositioningDescription(); 82 case TAG_X_RESOLUTION: 83 return getXResolutionDescription(); 84 case TAG_Y_RESOLUTION: 85 return getYResolutionDescription(); 72 case TAG_NEW_SUBFILE_TYPE: 73 return getNewSubfileTypeDescription(); 74 case TAG_SUBFILE_TYPE: 75 return getSubfileTypeDescription(); 86 76 case TAG_IMAGE_WIDTH: 87 77 return getImageWidthDescription(); … … 90 80 case TAG_BITS_PER_SAMPLE: 91 81 return getBitsPerSampleDescription(); 82 case TAG_COMPRESSION: 83 return getCompressionDescription(); 92 84 case TAG_PHOTOMETRIC_INTERPRETATION: 93 85 return getPhotometricInterpretationDescription(); 86 case TAG_THRESHOLDING: 87 return getThresholdingDescription(); 88 case TAG_FILL_ORDER: 89 return getFillOrderDescription(); 90 case TAG_ORIENTATION: 91 return getOrientationDescription(); 92 case TAG_SAMPLES_PER_PIXEL: 93 return getSamplesPerPixelDescription(); 94 94 case TAG_ROWS_PER_STRIP: 95 95 return getRowsPerStripDescription(); 96 96 case TAG_STRIP_BYTE_COUNTS: 97 97 return getStripByteCountsDescription(); 98 case TAG_SAMPLES_PER_PIXEL: 99 return getSamplesPerPixelDescription(); 98 case TAG_X_RESOLUTION: 99 return getXResolutionDescription(); 100 case TAG_Y_RESOLUTION: 101 return getYResolutionDescription(); 100 102 case TAG_PLANAR_CONFIGURATION: 101 103 return getPlanarConfigurationDescription(); 104 case TAG_RESOLUTION_UNIT: 105 return getResolutionDescription(); 106 case TAG_JPEG_PROC: 107 return getJpegProcDescription(); 102 108 case TAG_YCBCR_SUBSAMPLING: 103 109 return getYCbCrSubsamplingDescription(); 110 case TAG_YCBCR_POSITIONING: 111 return getYCbCrPositioningDescription(); 104 112 case TAG_REFERENCE_BLACK_WHITE: 105 113 return getReferenceBlackWhiteDescription(); 106 case TAG_WIN_AUTHOR:107 return getWindowsAuthorDescription();108 case TAG_WIN_COMMENT:109 return getWindowsCommentDescription();110 case TAG_WIN_KEYWORDS:111 return getWindowsKeywordsDescription();112 case TAG_WIN_SUBJECT:113 return getWindowsSubjectDescription();114 case TAG_WIN_TITLE:115 return getWindowsTitleDescription();116 case TAG_NEW_SUBFILE_TYPE:117 return getNewSubfileTypeDescription();118 case TAG_SUBFILE_TYPE:119 return getSubfileTypeDescription();120 case TAG_THRESHOLDING:121 return getThresholdingDescription();122 case TAG_FILL_ORDER:123 return getFillOrderDescription();124 114 case TAG_CFA_PATTERN_2: 125 115 return getCfaPattern2Description(); 126 116 case TAG_EXPOSURE_TIME: 127 117 return getExposureTimeDescription(); 118 case TAG_FNUMBER: 119 return getFNumberDescription(); 120 case TAG_EXPOSURE_PROGRAM: 121 return getExposureProgramDescription(); 122 case TAG_ISO_EQUIVALENT: 123 return getIsoEquivalentDescription(); 124 case TAG_SENSITIVITY_TYPE: 125 return getSensitivityTypeRangeDescription(); 126 case TAG_EXIF_VERSION: 127 return getExifVersionDescription(); 128 case TAG_COMPONENTS_CONFIGURATION: 129 return getComponentConfigurationDescription(); 130 case TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL: 131 return getCompressedAverageBitsPerPixelDescription(); 128 132 case TAG_SHUTTER_SPEED: 129 133 return getShutterSpeedDescription(); 130 case TAG_FNUMBER: 131 return getFNumberDescription(); 132 case TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL: 133 return getCompressedAverageBitsPerPixelDescription(); 134 case TAG_APERTURE: 135 return getApertureValueDescription(); 136 case TAG_BRIGHTNESS_VALUE: 137 return getBrightnessValueDescription(); 138 case TAG_EXPOSURE_BIAS: 139 return getExposureBiasDescription(); 140 case TAG_MAX_APERTURE: 141 return getMaxApertureValueDescription(); 134 142 case TAG_SUBJECT_DISTANCE: 135 143 return getSubjectDistanceDescription(); … … 142 150 case TAG_FOCAL_LENGTH: 143 151 return getFocalLengthDescription(); 152 case TAG_USER_COMMENT: 153 return getUserCommentDescription(); 154 case TAG_TEMPERATURE: 155 return getTemperatureDescription(); 156 case TAG_HUMIDITY: 157 return getHumidityDescription(); 158 case TAG_PRESSURE: 159 return getPressureDescription(); 160 case TAG_WATER_DEPTH: 161 return getWaterDepthDescription(); 162 case TAG_ACCELERATION: 163 return getAccelerationDescription(); 164 case TAG_CAMERA_ELEVATION_ANGLE: 165 return getCameraElevationAngleDescription(); 166 case TAG_WIN_TITLE: 167 return getWindowsTitleDescription(); 168 case TAG_WIN_COMMENT: 169 return getWindowsCommentDescription(); 170 case TAG_WIN_AUTHOR: 171 return getWindowsAuthorDescription(); 172 case TAG_WIN_KEYWORDS: 173 return getWindowsKeywordsDescription(); 174 case TAG_WIN_SUBJECT: 175 return getWindowsSubjectDescription(); 176 case TAG_FLASHPIX_VERSION: 177 return getFlashPixVersionDescription(); 144 178 case TAG_COLOR_SPACE: 145 179 return getColorSpaceDescription(); … … 148 182 case TAG_EXIF_IMAGE_HEIGHT: 149 183 return getExifImageHeightDescription(); 150 case TAG_FOCAL_PLANE_RESOLUTION_UNIT:151 return getFocalPlaneResolutionUnitDescription();152 184 case TAG_FOCAL_PLANE_X_RESOLUTION: 153 185 return getFocalPlaneXResolutionDescription(); 154 186 case TAG_FOCAL_PLANE_Y_RESOLUTION: 155 187 return getFocalPlaneYResolutionDescription(); 156 case TAG_EXPOSURE_PROGRAM: 157 return getExposureProgramDescription(); 158 case TAG_APERTURE: 159 return getApertureValueDescription(); 160 case TAG_MAX_APERTURE: 161 return getMaxApertureValueDescription(); 188 case TAG_FOCAL_PLANE_RESOLUTION_UNIT: 189 return getFocalPlaneResolutionUnitDescription(); 162 190 case TAG_SENSING_METHOD: 163 191 return getSensingMethodDescription(); 164 case TAG_EXPOSURE_BIAS:165 return getExposureBiasDescription();166 192 case TAG_FILE_SOURCE: 167 193 return getFileSourceDescription(); … … 170 196 case TAG_CFA_PATTERN: 171 197 return getCfaPatternDescription(); 172 case TAG_COMPONENTS_CONFIGURATION:173 return getComponentConfigurationDescription();174 case TAG_EXIF_VERSION:175 return getExifVersionDescription();176 case TAG_FLASHPIX_VERSION:177 return getFlashPixVersionDescription();178 case TAG_ISO_EQUIVALENT:179 return getIsoEquivalentDescription();180 case TAG_USER_COMMENT:181 return getUserCommentDescription();182 198 case TAG_CUSTOM_RENDERED: 183 199 return getCustomRenderedDescription(); … … 202 218 case TAG_SUBJECT_DISTANCE_RANGE: 203 219 return getSubjectDistanceRangeDescription(); 204 case TAG_SENSITIVITY_TYPE:205 return getSensitivityTypeRangeDescription();206 case TAG_COMPRESSION:207 return getCompressionDescription();208 case TAG_JPEG_PROC:209 return getJpegProcDescription();210 220 case TAG_LENS_SPECIFICATION: 211 221 return getLensSpecificationDescription(); … … 216 226 217 227 @Nullable 218 public String getInteropVersionDescription()219 {220 return getVersionBytesDescription(TAG_INTEROP_VERSION, 2);221 }222 223 @Nullable224 228 public String getInteropIndexDescription() 225 229 { … … 235 239 236 240 @Nullable 237 public String getReferenceBlackWhiteDescription() 238 { 239 // For some reason, sometimes this is read as a long[] and 240 // getIntArray isn't able to deal with it 241 int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE); 242 if (ints==null || ints.length < 6) 243 { 244 Object o = _directory.getObject(TAG_REFERENCE_BLACK_WHITE); 245 if (o != null && (o instanceof long[])) 246 { 247 long[] longs = (long[])o; 248 if (longs.length < 6) 249 return null; 250 251 ints = new int[longs.length]; 252 for (int i = 0; i < longs.length; i++) 253 ints[i] = (int)longs[i]; 254 } 255 else 256 return null; 257 } 258 259 int blackR = ints[0]; 260 int whiteR = ints[1]; 261 int blackG = ints[2]; 262 int whiteG = ints[3]; 263 int blackB = ints[4]; 264 int whiteB = ints[5]; 265 return String.format("[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB); 266 } 267 268 @Nullable 269 public String getYResolutionDescription() 270 { 271 Rational value = _directory.getRational(TAG_Y_RESOLUTION); 272 if (value==null) 273 return null; 274 final String unit = getResolutionDescription(); 275 return String.format("%s dots per %s", 276 value.toSimpleString(_allowDecimalRepresentationOfRationals), 277 unit == null ? "unit" : unit.toLowerCase()); 278 } 279 280 @Nullable 281 public String getXResolutionDescription() 282 { 283 Rational value = _directory.getRational(TAG_X_RESOLUTION); 284 if (value == null) 285 return null; 286 final String unit = getResolutionDescription(); 287 return String.format("%s dots per %s", 288 value.toSimpleString(_allowDecimalRepresentationOfRationals), 289 unit == null ? "unit" : unit.toLowerCase()); 290 } 291 292 @Nullable 293 public String getYCbCrPositioningDescription() 294 { 295 return getIndexedDescription(TAG_YCBCR_POSITIONING, 1, "Center of pixel array", "Datum point"); 296 } 297 298 @Nullable 299 public String getOrientationDescription() 300 { 301 return super.getOrientationDescription(TAG_ORIENTATION); 302 } 303 304 @Nullable 305 public String getResolutionDescription() 306 { 307 // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch) 308 return getIndexedDescription(TAG_RESOLUTION_UNIT, 1, "(No unit)", "Inch", "cm"); 309 } 310 311 /** The Windows specific tags uses plain Unicode. */ 312 @Nullable 313 private String getUnicodeDescription(int tag) 314 { 315 byte[] bytes = _directory.getByteArray(tag); 316 if (bytes == null) 317 return null; 318 try { 319 // Decode the unicode string and trim the unicode zero "\0" from the end. 320 return new String(bytes, "UTF-16LE").trim(); 321 } catch (UnsupportedEncodingException ex) { 322 return null; 323 } 324 } 325 326 @Nullable 327 public String getWindowsAuthorDescription() 328 { 329 return getUnicodeDescription(TAG_WIN_AUTHOR); 330 } 331 332 @Nullable 333 public String getWindowsCommentDescription() 334 { 335 return getUnicodeDescription(TAG_WIN_COMMENT); 336 } 337 338 @Nullable 339 public String getWindowsKeywordsDescription() 340 { 341 return getUnicodeDescription(TAG_WIN_KEYWORDS); 342 } 343 344 @Nullable 345 public String getWindowsTitleDescription() 346 { 347 return getUnicodeDescription(TAG_WIN_TITLE); 348 } 349 350 @Nullable 351 public String getWindowsSubjectDescription() 352 { 353 return getUnicodeDescription(TAG_WIN_SUBJECT); 354 } 355 356 @Nullable 357 public String getYCbCrSubsamplingDescription() 358 { 359 int[] positions = _directory.getIntArray(TAG_YCBCR_SUBSAMPLING); 360 if (positions == null || positions.length < 2) 361 return null; 362 if (positions[0] == 2 && positions[1] == 1) { 363 return "YCbCr4:2:2"; 364 } else if (positions[0] == 2 && positions[1] == 2) { 365 return "YCbCr4:2:0"; 366 } else { 367 return "(Unknown)"; 368 } 369 } 370 371 @Nullable 372 public String getPlanarConfigurationDescription() 373 { 374 // When image format is no compression YCbCr, this value shows byte aligns of YCbCr 375 // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling 376 // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr 377 // plane format. 378 return getIndexedDescription(TAG_PLANAR_CONFIGURATION, 379 1, 380 "Chunky (contiguous for each subsampling pixel)", 381 "Separate (Y-plane/Cb-plane/Cr-plane format)" 382 ); 383 } 384 385 @Nullable 386 public String getSamplesPerPixelDescription() 387 { 388 String value = _directory.getString(TAG_SAMPLES_PER_PIXEL); 389 return value == null ? null : value + " samples/pixel"; 390 } 391 392 @Nullable 393 public String getRowsPerStripDescription() 394 { 395 final String value = _directory.getString(TAG_ROWS_PER_STRIP); 396 return value == null ? null : value + " rows/strip"; 397 } 398 399 @Nullable 400 public String getStripByteCountsDescription() 401 { 402 final String value = _directory.getString(TAG_STRIP_BYTE_COUNTS); 403 return value == null ? null : value + " bytes"; 404 } 405 406 @Nullable 407 public String getPhotometricInterpretationDescription() 408 { 409 // Shows the color space of the image data components 410 Integer value = _directory.getInteger(TAG_PHOTOMETRIC_INTERPRETATION); 411 if (value == null) 412 return null; 413 switch (value) { 414 case 0: return "WhiteIsZero"; 415 case 1: return "BlackIsZero"; 416 case 2: return "RGB"; 417 case 3: return "RGB Palette"; 418 case 4: return "Transparency Mask"; 419 case 5: return "CMYK"; 420 case 6: return "YCbCr"; 421 case 8: return "CIELab"; 422 case 9: return "ICCLab"; 423 case 10: return "ITULab"; 424 case 32803: return "Color Filter Array"; 425 case 32844: return "Pixar LogL"; 426 case 32845: return "Pixar LogLuv"; 427 case 32892: return "Linear Raw"; 428 default: 429 return "Unknown colour space"; 430 } 431 } 432 433 @Nullable 434 public String getBitsPerSampleDescription() 435 { 436 String value = _directory.getString(TAG_BITS_PER_SAMPLE); 437 return value == null ? null : value + " bits/component/pixel"; 438 } 439 440 @Nullable 441 public String getImageWidthDescription() 442 { 443 String value = _directory.getString(TAG_IMAGE_WIDTH); 444 return value == null ? null : value + " pixels"; 445 } 446 447 @Nullable 448 public String getImageHeightDescription() 449 { 450 String value = _directory.getString(TAG_IMAGE_HEIGHT); 451 return value == null ? null : value + " pixels"; 241 public String getInteropVersionDescription() 242 { 243 return getVersionBytesDescription(TAG_INTEROP_VERSION, 2); 452 244 } 453 245 … … 478 270 479 271 @Nullable 480 public String getThresholdingDescription() 481 { 482 return getIndexedDescription(TAG_THRESHOLDING, 1, 483 "No dithering or halftoning", 484 "Ordered dither or halftone", 485 "Randomized dither" 486 ); 487 } 488 489 @Nullable 490 public String getFillOrderDescription() 491 { 492 return getIndexedDescription(TAG_FILL_ORDER, 1, 493 "Normal", 494 "Reversed" 495 ); 496 } 497 498 @Nullable 499 public String getSubjectDistanceRangeDescription() 500 { 501 return getIndexedDescription(TAG_SUBJECT_DISTANCE_RANGE, 502 "Unknown", 503 "Macro", 504 "Close view", 505 "Distant view" 506 ); 507 } 508 509 @Nullable 510 public String getSensitivityTypeRangeDescription() 511 { 512 return getIndexedDescription(TAG_SENSITIVITY_TYPE, 513 "Unknown", 514 "Standard Output Sensitivity", 515 "Recommended Exposure Index", 516 "ISO Speed", 517 "Standard Output Sensitivity and Recommended Exposure Index", 518 "Standard Output Sensitivity and ISO Speed", 519 "Recommended Exposure Index and ISO Speed", 520 "Standard Output Sensitivity, Recommended Exposure Index and ISO Speed" 521 ); 522 } 523 524 @Nullable 525 public String getLensSpecificationDescription() 526 { 527 return getLensSpecificationDescription(TAG_LENS_SPECIFICATION); 528 } 529 530 @Nullable 531 public String getSharpnessDescription() 532 { 533 return getIndexedDescription(TAG_SHARPNESS, 534 "None", 535 "Low", 536 "Hard" 537 ); 538 } 539 540 @Nullable 541 public String getSaturationDescription() 542 { 543 return getIndexedDescription(TAG_SATURATION, 544 "None", 545 "Low saturation", 546 "High saturation" 547 ); 548 } 549 550 @Nullable 551 public String getContrastDescription() 552 { 553 return getIndexedDescription(TAG_CONTRAST, 554 "None", 555 "Soft", 556 "Hard" 557 ); 558 } 559 560 @Nullable 561 public String getGainControlDescription() 562 { 563 return getIndexedDescription(TAG_GAIN_CONTROL, 564 "None", 565 "Low gain up", 566 "Low gain down", 567 "High gain up", 568 "High gain down" 569 ); 570 } 571 572 @Nullable 573 public String getSceneCaptureTypeDescription() 574 { 575 return getIndexedDescription(TAG_SCENE_CAPTURE_TYPE, 576 "Standard", 577 "Landscape", 578 "Portrait", 579 "Night scene" 580 ); 581 } 582 583 @Nullable 584 public String get35mmFilmEquivFocalLengthDescription() 585 { 586 Integer value = _directory.getInteger(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH); 587 return value == null 588 ? null 589 : value == 0 590 ? "Unknown" 591 : getFocalLengthDescription(value); 592 } 593 594 @Nullable 595 public String getDigitalZoomRatioDescription() 596 { 597 Rational value = _directory.getRational(TAG_DIGITAL_ZOOM_RATIO); 598 return value == null 599 ? null 600 : value.getNumerator() == 0 601 ? "Digital zoom not used" 602 : new DecimalFormat("0.#").format(value.doubleValue()); 603 } 604 605 @Nullable 606 public String getWhiteBalanceModeDescription() 607 { 608 return getIndexedDescription(TAG_WHITE_BALANCE_MODE, 609 "Auto white balance", 610 "Manual white balance" 611 ); 612 } 613 614 @Nullable 615 public String getExposureModeDescription() 616 { 617 return getIndexedDescription(TAG_EXPOSURE_MODE, 618 "Auto exposure", 619 "Manual exposure", 620 "Auto bracket" 621 ); 622 } 623 624 @Nullable 625 public String getCustomRenderedDescription() 626 { 627 return getIndexedDescription(TAG_CUSTOM_RENDERED, 628 "Normal process", 629 "Custom process" 630 ); 631 } 632 633 @Nullable 634 public String getUserCommentDescription() 635 { 636 byte[] commentBytes = _directory.getByteArray(TAG_USER_COMMENT); 637 if (commentBytes == null) 638 return null; 639 if (commentBytes.length == 0) 640 return ""; 641 642 final Map<String, String> encodingMap = new HashMap<String, String>(); 643 encodingMap.put("ASCII", System.getProperty("file.encoding")); // Someone suggested "ISO-8859-1". 644 encodingMap.put("UNICODE", "UTF-16LE"); 645 encodingMap.put("JIS", "Shift-JIS"); // We assume this charset for now. Another suggestion is "JIS". 646 647 try { 648 if (commentBytes.length >= 10) { 649 String firstTenBytesString = new String(commentBytes, 0, 10); 650 651 // try each encoding name 652 for (Map.Entry<String, String> pair : encodingMap.entrySet()) { 653 String encodingName = pair.getKey(); 654 String charset = pair.getValue(); 655 if (firstTenBytesString.startsWith(encodingName)) { 656 // skip any null or blank characters commonly present after the encoding name, up to a limit of 10 from the start 657 for (int j = encodingName.length(); j < 10; j++) { 658 byte b = commentBytes[j]; 659 if (b != '\0' && b != ' ') 660 return new String(commentBytes, j, commentBytes.length - j, charset).trim(); 661 } 662 return new String(commentBytes, 10, commentBytes.length - 10, charset).trim(); 663 } 664 } 665 } 666 // special handling fell through, return a plain string representation 667 return new String(commentBytes, System.getProperty("file.encoding")).trim(); 668 } catch (UnsupportedEncodingException ex) { 669 return null; 670 } 671 } 672 673 @Nullable 674 public String getIsoEquivalentDescription() 675 { 676 // Have seen an exception here from files produced by ACDSEE that stored an int[] here with two values 677 Integer isoEquiv = _directory.getInteger(TAG_ISO_EQUIVALENT); 678 // There used to be a check here that multiplied ISO values < 50 by 200. 679 // Issue 36 shows a smart-phone image from a Samsung Galaxy S2 with ISO-40. 680 return isoEquiv != null 681 ? Integer.toString(isoEquiv) 682 : null; 683 } 684 685 @Nullable 686 public String getExifVersionDescription() 687 { 688 return getVersionBytesDescription(TAG_EXIF_VERSION, 2); 689 } 690 691 @Nullable 692 public String getFlashPixVersionDescription() 693 { 694 return getVersionBytesDescription(TAG_FLASHPIX_VERSION, 2); 695 } 696 697 @Nullable 698 public String getSceneTypeDescription() 699 { 700 return getIndexedDescription(TAG_SCENE_TYPE, 701 1, 702 "Directly photographed image" 703 ); 704 } 705 706 /// <summary> 707 /// String description of CFA Pattern 708 /// </summary> 709 /// <remarks> 710 /// Converted from Exiftool version 10.33 created by Phil Harvey 711 /// http://www.sno.phy.queensu.ca/~phil/exiftool/ 712 /// lib\Image\ExifTool\Exif.pm 713 /// 714 /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. 715 /// It does not apply to all sensing methods. 716 /// </remarks> 717 @Nullable 718 public String getCfaPatternDescription() 719 { 720 return formatCFAPattern(decodeCfaPattern(TAG_CFA_PATTERN)); 721 } 722 723 /// <summary> 724 /// String description of CFA Pattern 725 /// </summary> 726 /// <remarks> 727 /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. 728 /// It does not apply to all sensing methods. 729 /// 730 /// ExifDirectoryBase.TAG_CFA_PATTERN_2 holds only the pixel pattern. ExifDirectoryBase.TAG_CFA_REPEAT_PATTERN_DIM is expected to exist and pass 731 /// some conditional tests. 732 /// </remarks> 733 @Nullable 734 public String getCfaPattern2Description() 735 { 736 byte[] values = _directory.getByteArray(TAG_CFA_PATTERN_2); 737 if (values == null) 738 return null; 739 740 int[] repeatPattern = _directory.getIntArray(TAG_CFA_REPEAT_PATTERN_DIM); 741 if (repeatPattern == null) 742 return String.format("Repeat Pattern not found for CFAPattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); 743 744 if (repeatPattern.length == 2 && values.length == (repeatPattern[0] * repeatPattern[1])) 745 { 746 int[] intpattern = new int[2 + values.length]; 747 intpattern[0] = repeatPattern[0]; 748 intpattern[1] = repeatPattern[1]; 749 750 for (int i = 0; i < values.length; i++) 751 intpattern[i + 2] = values[i] & 0xFF; // convert the values[i] byte to unsigned 752 753 return formatCFAPattern(intpattern); 754 } 755 756 return String.format("Unknown Pattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); 757 } 758 759 @Nullable 760 private static String formatCFAPattern(@Nullable int[] pattern) 761 { 762 if (pattern == null) 763 return null; 764 if (pattern.length < 2) 765 return "<truncated data>"; 766 if (pattern[0] == 0 && pattern[1] == 0) 767 return "<zero pattern size>"; 768 769 int end = 2 + pattern[0] * pattern[1]; 770 if (end > pattern.length) 771 return "<invalid pattern size>"; 772 773 String[] cfaColors = { "Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "White" }; 774 775 StringBuilder ret = new StringBuilder(); 776 ret.append("["); 777 for (int pos = 2; pos < end; pos++) 778 { 779 if (pattern[pos] <= cfaColors.length - 1) 780 ret.append(cfaColors[pattern[pos]]); 781 else 782 ret.append("Unknown"); // indicated pattern position is outside the array bounds 783 784 if ((pos - 2) % pattern[1] == 0) 785 ret.append(","); 786 else if(pos != end - 1) 787 ret.append("]["); 788 } 789 ret.append("]"); 790 791 return ret.toString(); 792 } 793 794 /// <summary> 795 /// Decode raw CFAPattern value 796 /// </summary> 797 /// <remarks> 798 /// Converted from Exiftool version 10.33 created by Phil Harvey 799 /// http://www.sno.phy.queensu.ca/~phil/exiftool/ 800 /// lib\Image\ExifTool\Exif.pm 801 /// 802 /// The value consists of: 803 /// - Two short, being the grid width and height of the repeated pattern. 804 /// - Next, for every pixel in that pattern, an identification code. 805 /// </remarks> 806 @Nullable 807 private int[] decodeCfaPattern(int tagType) 808 { 809 int[] ret; 810 811 byte[] values = _directory.getByteArray(tagType); 812 if (values == null) 813 return null; 814 815 if (values.length < 4) 816 { 817 ret = new int[values.length]; 818 for (int i = 0; i < values.length; i++) 819 ret[i] = values[i]; 820 return ret; 821 } 822 823 ret = new int[values.length - 2]; 824 825 try { 826 ByteArrayReader reader = new ByteArrayReader(values); 827 828 // first two values should be read as 16-bits (2 bytes) 829 short item0 = reader.getInt16(0); 830 short item1 = reader.getInt16(2); 831 832 Boolean copyArray = false; 833 int end = 2 + item0 * item1; 834 if (end > values.length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values 835 { 836 // try swapping byte order (I have seen this order different than in EXIF) 837 reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder()); 838 item0 = reader.getInt16(0); 839 item1 = reader.getInt16(2); 840 841 if (values.length >= (2 + item0 * item1)) 842 copyArray = true; 843 } 844 else 845 copyArray = true; 846 847 if(copyArray) 848 { 849 ret[0] = item0; 850 ret[1] = item1; 851 852 for (int i = 4; i < values.length; i++) 853 ret[i - 2] = reader.getInt8(i); 854 } 855 } catch (IOException ex) { 856 _directory.addError("IO exception processing data: " + ex.getMessage()); 857 } 858 859 return ret; 860 } 861 862 @Nullable 863 public String getFileSourceDescription() 864 { 865 return getIndexedDescription(TAG_FILE_SOURCE, 866 1, 867 "Film Scanner", 868 "Reflection Print Scanner", 869 "Digital Still Camera (DSC)" 870 ); 871 } 872 873 @Nullable 874 public String getExposureBiasDescription() 875 { 876 Rational value = _directory.getRational(TAG_EXPOSURE_BIAS); 877 if (value == null) 878 return null; 879 return value.toSimpleString(true) + " EV"; 880 } 881 882 @Nullable 883 public String getMaxApertureValueDescription() 884 { 885 Double aperture = _directory.getDoubleObject(TAG_MAX_APERTURE); 886 if (aperture == null) 887 return null; 888 double fStop = PhotographicConversions.apertureToFStop(aperture); 889 return getFStopDescription(fStop); 890 } 891 892 @Nullable 893 public String getApertureValueDescription() 894 { 895 Double aperture = _directory.getDoubleObject(TAG_APERTURE); 896 if (aperture == null) 897 return null; 898 double fStop = PhotographicConversions.apertureToFStop(aperture); 899 return getFStopDescription(fStop); 900 } 901 902 @Nullable 903 public String getExposureProgramDescription() 904 { 905 return getIndexedDescription(TAG_EXPOSURE_PROGRAM, 906 1, 907 "Manual control", 908 "Program normal", 909 "Aperture priority", 910 "Shutter priority", 911 "Program creative (slow program)", 912 "Program action (high-speed program)", 913 "Portrait mode", 914 "Landscape mode" 915 ); 916 } 917 918 919 @Nullable 920 public String getFocalPlaneXResolutionDescription() 921 { 922 Rational rational = _directory.getRational(TAG_FOCAL_PLANE_X_RESOLUTION); 923 if (rational == null) 924 return null; 925 final String unit = getFocalPlaneResolutionUnitDescription(); 926 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) 927 + (unit == null ? "" : " " + unit.toLowerCase()); 928 } 929 930 @Nullable 931 public String getFocalPlaneYResolutionDescription() 932 { 933 Rational rational = _directory.getRational(TAG_FOCAL_PLANE_Y_RESOLUTION); 934 if (rational == null) 935 return null; 936 final String unit = getFocalPlaneResolutionUnitDescription(); 937 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) 938 + (unit == null ? "" : " " + unit.toLowerCase()); 939 } 940 941 @Nullable 942 public String getFocalPlaneResolutionUnitDescription() 943 { 944 // Unit of FocalPlaneXResolution/FocalPlaneYResolution. 945 // '1' means no-unit, '2' inch, '3' centimeter. 946 return getIndexedDescription(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 947 1, 948 "(No unit)", 949 "Inches", 950 "cm" 951 ); 952 } 953 954 @Nullable 955 public String getExifImageWidthDescription() 956 { 957 final Integer value = _directory.getInteger(TAG_EXIF_IMAGE_WIDTH); 272 public String getImageWidthDescription() 273 { 274 String value = _directory.getString(TAG_IMAGE_WIDTH); 958 275 return value == null ? null : value + " pixels"; 959 276 } 960 277 961 278 @Nullable 962 public String get ExifImageHeightDescription()963 { 964 final Integer value = _directory.getInteger(TAG_EXIF_IMAGE_HEIGHT);279 public String getImageHeightDescription() 280 { 281 String value = _directory.getString(TAG_IMAGE_HEIGHT); 965 282 return value == null ? null : value + " pixels"; 966 283 } 967 284 968 285 @Nullable 969 public String getColorSpaceDescription() 970 { 971 final Integer value = _directory.getInteger(TAG_COLOR_SPACE); 972 if (value == null) 973 return null; 974 if (value == 1) 975 return "sRGB"; 976 if (value == 65535) 977 return "Undefined"; 978 return "Unknown (" + value + ")"; 979 } 980 981 @Nullable 982 public String getFocalLengthDescription() 983 { 984 Rational value = _directory.getRational(TAG_FOCAL_LENGTH); 985 return value == null ? null : getFocalLengthDescription(value.doubleValue()); 986 } 987 988 @Nullable 989 public String getFlashDescription() 990 { 991 /* 992 * This is a bit mask. 993 * 0 = flash fired 994 * 1 = return detected 995 * 2 = return able to be detected 996 * 3 = unknown 997 * 4 = auto used 998 * 5 = unknown 999 * 6 = red eye reduction used 1000 */ 1001 1002 final Integer value = _directory.getInteger(TAG_FLASH); 1003 1004 if (value == null) 1005 return null; 1006 1007 StringBuilder sb = new StringBuilder(); 1008 1009 if ((value & 0x1) != 0) 1010 sb.append("Flash fired"); 1011 else 1012 sb.append("Flash did not fire"); 1013 1014 // check if we're able to detect a return, before we mention it 1015 if ((value & 0x4) != 0) { 1016 if ((value & 0x2) != 0) 1017 sb.append(", return detected"); 1018 else 1019 sb.append(", return not detected"); 1020 } 1021 1022 if ((value & 0x10) != 0) 1023 sb.append(", auto"); 1024 1025 if ((value & 0x40) != 0) 1026 sb.append(", red-eye reduction"); 1027 1028 return sb.toString(); 1029 } 1030 1031 @Nullable 1032 public String getWhiteBalanceDescription() 1033 { 1034 // See http://web.archive.org/web/20131018091152/http://exif.org/Exif2-2.PDF page 35 1035 final Integer value = _directory.getInteger(TAG_WHITE_BALANCE); 1036 if (value == null) 1037 return null; 1038 switch (value) { 1039 case 0: return "Unknown"; 1040 case 1: return "Daylight"; 1041 case 2: return "Florescent"; 1042 case 3: return "Tungsten"; 1043 case 4: return "Flash"; 1044 case 9: return "Fine Weather"; 1045 case 10: return "Cloudy"; 1046 case 11: return "Shade"; 1047 case 12: return "Daylight Fluorescent"; 1048 case 13: return "Day White Fluorescent"; 1049 case 14: return "Cool White Fluorescent"; 1050 case 15: return "White Fluorescent"; 1051 case 16: return "Warm White Fluorescent"; 1052 case 17: return "Standard light"; 1053 case 18: return "Standard light (B)"; 1054 case 19: return "Standard light (C)"; 1055 case 20: return "D55"; 1056 case 21: return "D65"; 1057 case 22: return "D75"; 1058 case 23: return "D50"; 1059 case 24: return "Studio Tungsten"; 1060 case 255: return "(Other)"; 1061 default: 1062 return "Unknown (" + value + ")"; 1063 } 1064 } 1065 1066 @Nullable 1067 public String getMeteringModeDescription() 1068 { 1069 // '0' means unknown, '1' average, '2' center weighted average, '3' spot 1070 // '4' multi-spot, '5' multi-segment, '6' partial, '255' other 1071 Integer value = _directory.getInteger(TAG_METERING_MODE); 1072 if (value == null) 1073 return null; 1074 switch (value) { 1075 case 0: return "Unknown"; 1076 case 1: return "Average"; 1077 case 2: return "Center weighted average"; 1078 case 3: return "Spot"; 1079 case 4: return "Multi-spot"; 1080 case 5: return "Multi-segment"; 1081 case 6: return "Partial"; 1082 case 255: return "(Other)"; 1083 default: 1084 return "Unknown (" + value + ")"; 1085 } 286 public String getBitsPerSampleDescription() 287 { 288 String value = _directory.getString(TAG_BITS_PER_SAMPLE); 289 return value == null ? null : value + " bits/component/pixel"; 1086 290 } 1087 291 … … 1140 344 1141 345 @Nullable 1142 public String getSubjectDistanceDescription() 1143 { 1144 Rational value = _directory.getRational(TAG_SUBJECT_DISTANCE); 1145 if (value == null) 1146 return null; 1147 DecimalFormat formatter = new DecimalFormat("0.0##"); 1148 return formatter.format(value.doubleValue()) + " metres"; 346 public String getPhotometricInterpretationDescription() 347 { 348 // Shows the color space of the image data components 349 Integer value = _directory.getInteger(TAG_PHOTOMETRIC_INTERPRETATION); 350 if (value == null) 351 return null; 352 switch (value) { 353 case 0: return "WhiteIsZero"; 354 case 1: return "BlackIsZero"; 355 case 2: return "RGB"; 356 case 3: return "RGB Palette"; 357 case 4: return "Transparency Mask"; 358 case 5: return "CMYK"; 359 case 6: return "YCbCr"; 360 case 8: return "CIELab"; 361 case 9: return "ICCLab"; 362 case 10: return "ITULab"; 363 case 32803: return "Color Filter Array"; 364 case 32844: return "Pixar LogL"; 365 case 32845: return "Pixar LogLuv"; 366 case 32892: return "Linear Raw"; 367 default: 368 return "Unknown colour space"; 369 } 370 } 371 372 @Nullable 373 public String getThresholdingDescription() 374 { 375 return getIndexedDescription(TAG_THRESHOLDING, 1, 376 "No dithering or halftoning", 377 "Ordered dither or halftone", 378 "Randomized dither" 379 ); 380 } 381 382 @Nullable 383 public String getFillOrderDescription() 384 { 385 return getIndexedDescription(TAG_FILL_ORDER, 1, 386 "Normal", 387 "Reversed" 388 ); 389 } 390 391 @Nullable 392 public String getOrientationDescription() 393 { 394 return super.getOrientationDescription(TAG_ORIENTATION); 395 } 396 397 @Nullable 398 public String getSamplesPerPixelDescription() 399 { 400 String value = _directory.getString(TAG_SAMPLES_PER_PIXEL); 401 return value == null ? null : value + " samples/pixel"; 402 } 403 404 @Nullable 405 public String getRowsPerStripDescription() 406 { 407 final String value = _directory.getString(TAG_ROWS_PER_STRIP); 408 return value == null ? null : value + " rows/strip"; 409 } 410 411 @Nullable 412 public String getStripByteCountsDescription() 413 { 414 final String value = _directory.getString(TAG_STRIP_BYTE_COUNTS); 415 return value == null ? null : value + " bytes"; 416 } 417 418 @Nullable 419 public String getXResolutionDescription() 420 { 421 Rational value = _directory.getRational(TAG_X_RESOLUTION); 422 if (value == null) 423 return null; 424 final String unit = getResolutionDescription(); 425 return String.format("%s dots per %s", 426 value.toSimpleString(_allowDecimalRepresentationOfRationals), 427 unit == null ? "unit" : unit.toLowerCase()); 428 } 429 430 @Nullable 431 public String getYResolutionDescription() 432 { 433 Rational value = _directory.getRational(TAG_Y_RESOLUTION); 434 if (value==null) 435 return null; 436 final String unit = getResolutionDescription(); 437 return String.format("%s dots per %s", 438 value.toSimpleString(_allowDecimalRepresentationOfRationals), 439 unit == null ? "unit" : unit.toLowerCase()); 440 } 441 442 @Nullable 443 public String getPlanarConfigurationDescription() 444 { 445 // When image format is no compression YCbCr, this value shows byte aligns of YCbCr 446 // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling 447 // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr 448 // plane format. 449 return getIndexedDescription(TAG_PLANAR_CONFIGURATION, 450 1, 451 "Chunky (contiguous for each subsampling pixel)", 452 "Separate (Y-plane/Cb-plane/Cr-plane format)" 453 ); 454 } 455 456 @Nullable 457 public String getResolutionDescription() 458 { 459 // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch) 460 return getIndexedDescription(TAG_RESOLUTION_UNIT, 1, "(No unit)", "Inch", "cm"); 461 } 462 463 @Nullable 464 public String getJpegProcDescription() 465 { 466 Integer value = _directory.getInteger(TAG_JPEG_PROC); 467 if (value == null) 468 return null; 469 switch (value) { 470 case 1: return "Baseline"; 471 case 14: return "Lossless"; 472 default: 473 return "Unknown (" + value + ")"; 474 } 475 } 476 477 @Nullable 478 public String getYCbCrSubsamplingDescription() 479 { 480 int[] positions = _directory.getIntArray(TAG_YCBCR_SUBSAMPLING); 481 if (positions == null || positions.length < 2) 482 return null; 483 if (positions[0] == 2 && positions[1] == 1) { 484 return "YCbCr4:2:2"; 485 } else if (positions[0] == 2 && positions[1] == 2) { 486 return "YCbCr4:2:0"; 487 } else { 488 return "(Unknown)"; 489 } 490 } 491 492 @Nullable 493 public String getYCbCrPositioningDescription() 494 { 495 return getIndexedDescription(TAG_YCBCR_POSITIONING, 1, "Center of pixel array", "Datum point"); 496 } 497 498 @Nullable 499 public String getReferenceBlackWhiteDescription() 500 { 501 // For some reason, sometimes this is read as a long[] and 502 // getIntArray isn't able to deal with it 503 int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE); 504 if (ints==null || ints.length < 6) 505 { 506 Object o = _directory.getObject(TAG_REFERENCE_BLACK_WHITE); 507 if (o != null && (o instanceof long[])) 508 { 509 long[] longs = (long[])o; 510 if (longs.length < 6) 511 return null; 512 513 ints = new int[longs.length]; 514 for (int i = 0; i < longs.length; i++) 515 ints[i] = (int)longs[i]; 516 } 517 else 518 return null; 519 } 520 521 int blackR = ints[0]; 522 int whiteR = ints[1]; 523 int blackG = ints[2]; 524 int whiteG = ints[3]; 525 int blackB = ints[4]; 526 int whiteB = ints[5]; 527 return String.format("[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB); 528 } 529 530 /// <summary> 531 /// String description of CFA Pattern 532 /// </summary> 533 /// <remarks> 534 /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. 535 /// It does not apply to all sensing methods. 536 /// 537 /// ExifDirectoryBase.TAG_CFA_PATTERN_2 holds only the pixel pattern. ExifDirectoryBase.TAG_CFA_REPEAT_PATTERN_DIM is expected to exist and pass 538 /// some conditional tests. 539 /// </remarks> 540 @Nullable 541 public String getCfaPattern2Description() 542 { 543 byte[] values = _directory.getByteArray(TAG_CFA_PATTERN_2); 544 if (values == null) 545 return null; 546 547 int[] repeatPattern = _directory.getIntArray(TAG_CFA_REPEAT_PATTERN_DIM); 548 if (repeatPattern == null) 549 return String.format("Repeat Pattern not found for CFAPattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); 550 551 if (repeatPattern.length == 2 && values.length == (repeatPattern[0] * repeatPattern[1])) 552 { 553 int[] intpattern = new int[2 + values.length]; 554 intpattern[0] = repeatPattern[0]; 555 intpattern[1] = repeatPattern[1]; 556 557 for (int i = 0; i < values.length; i++) 558 intpattern[i + 2] = values[i] & 0xFF; // convert the values[i] byte to unsigned 559 560 return formatCFAPattern(intpattern); 561 } 562 563 return String.format("Unknown Pattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); 564 } 565 566 @Nullable 567 private static String formatCFAPattern(@Nullable int[] pattern) 568 { 569 if (pattern == null) 570 return null; 571 if (pattern.length < 2) 572 return "<truncated data>"; 573 if (pattern[0] == 0 && pattern[1] == 0) 574 return "<zero pattern size>"; 575 576 int end = 2 + pattern[0] * pattern[1]; 577 if (end > pattern.length) 578 return "<invalid pattern size>"; 579 580 String[] cfaColors = { "Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "White" }; 581 582 StringBuilder ret = new StringBuilder(); 583 ret.append("["); 584 for (int pos = 2; pos < end; pos++) 585 { 586 if (pattern[pos] <= cfaColors.length - 1) 587 ret.append(cfaColors[pattern[pos]]); 588 else 589 ret.append("Unknown"); // indicated pattern position is outside the array bounds 590 591 if ((pos - 2) % pattern[1] == 0) 592 ret.append(","); 593 else if(pos != end - 1) 594 ret.append("]["); 595 } 596 ret.append("]"); 597 598 return ret.toString(); 599 } 600 601 @Nullable 602 public String getExposureTimeDescription() 603 { 604 String value = _directory.getString(TAG_EXPOSURE_TIME); 605 return value == null ? null : value + " sec"; 606 } 607 608 @Nullable 609 public String getFNumberDescription() 610 { 611 Rational value = _directory.getRational(TAG_FNUMBER); 612 if (value == null) 613 return null; 614 return getFStopDescription(value.doubleValue()); 615 } 616 617 @Nullable 618 public String getExposureProgramDescription() 619 { 620 return getIndexedDescription(TAG_EXPOSURE_PROGRAM, 621 1, 622 "Manual control", 623 "Program normal", 624 "Aperture priority", 625 "Shutter priority", 626 "Program creative (slow program)", 627 "Program action (high-speed program)", 628 "Portrait mode", 629 "Landscape mode" 630 ); 631 } 632 633 @Nullable 634 public String getIsoEquivalentDescription() 635 { 636 // Have seen an exception here from files produced by ACDSEE that stored an int[] here with two values 637 Integer isoEquiv = _directory.getInteger(TAG_ISO_EQUIVALENT); 638 // There used to be a check here that multiplied ISO values < 50 by 200. 639 // Issue 36 shows a smart-phone image from a Samsung Galaxy S2 with ISO-40. 640 return isoEquiv != null 641 ? Integer.toString(isoEquiv) 642 : null; 643 } 644 645 @Nullable 646 public String getSensitivityTypeRangeDescription() 647 { 648 return getIndexedDescription(TAG_SENSITIVITY_TYPE, 649 "Unknown", 650 "Standard Output Sensitivity", 651 "Recommended Exposure Index", 652 "ISO Speed", 653 "Standard Output Sensitivity and Recommended Exposure Index", 654 "Standard Output Sensitivity and ISO Speed", 655 "Recommended Exposure Index and ISO Speed", 656 "Standard Output Sensitivity, Recommended Exposure Index and ISO Speed" 657 ); 658 } 659 660 @Nullable 661 public String getExifVersionDescription() 662 { 663 return getVersionBytesDescription(TAG_EXIF_VERSION, 2); 664 } 665 666 @Nullable 667 public String getComponentConfigurationDescription() 668 { 669 int[] components = _directory.getIntArray(TAG_COMPONENTS_CONFIGURATION); 670 if (components == null) 671 return null; 672 String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"}; 673 StringBuilder componentConfig = new StringBuilder(); 674 for (int i = 0; i < Math.min(4, components.length); i++) { 675 int j = components[i]; 676 if (j > 0 && j < componentStrings.length) { 677 componentConfig.append(componentStrings[j]); 678 } 679 } 680 return componentConfig.toString(); 1149 681 } 1150 682 … … 1162 694 1163 695 @Nullable 1164 public String getExposureTimeDescription()1165 {1166 String value = _directory.getString(TAG_EXPOSURE_TIME);1167 return value == null ? null : value + " sec";1168 }1169 1170 @Nullable1171 696 public String getShutterSpeedDescription() 1172 697 { … … 1175 700 1176 701 @Nullable 1177 public String getFNumberDescription() 1178 { 1179 Rational value = _directory.getRational(TAG_FNUMBER); 1180 if (value == null) 1181 return null; 1182 return getFStopDescription(value.doubleValue()); 702 public String getApertureValueDescription() 703 { 704 Double aperture = _directory.getDoubleObject(TAG_APERTURE); 705 if (aperture == null) 706 return null; 707 double fStop = PhotographicConversions.apertureToFStop(aperture); 708 return getFStopDescription(fStop); 709 } 710 711 @Nullable 712 public String getBrightnessValueDescription() 713 { 714 Rational value = _directory.getRational(TAG_BRIGHTNESS_VALUE); 715 if (value == null) 716 return null; 717 if (value.getNumerator() == 0xFFFFFFFFL) 718 return "Unknown"; 719 DecimalFormat formatter = new DecimalFormat("0.0##"); 720 return formatter.format(value.doubleValue()); 721 } 722 723 @Nullable 724 public String getExposureBiasDescription() 725 { 726 Rational value = _directory.getRational(TAG_EXPOSURE_BIAS); 727 if (value == null) 728 return null; 729 return value.toSimpleString(true) + " EV"; 730 } 731 732 @Nullable 733 public String getMaxApertureValueDescription() 734 { 735 Double aperture = _directory.getDoubleObject(TAG_MAX_APERTURE); 736 if (aperture == null) 737 return null; 738 double fStop = PhotographicConversions.apertureToFStop(aperture); 739 return getFStopDescription(fStop); 740 } 741 742 @Nullable 743 public String getSubjectDistanceDescription() 744 { 745 Rational value = _directory.getRational(TAG_SUBJECT_DISTANCE); 746 if (value == null) 747 return null; 748 if (value.getNumerator() == 0xFFFFFFFFL) 749 return "Infinity"; 750 if (value.getNumerator() == 0) 751 return "Unknown"; 752 DecimalFormat formatter = new DecimalFormat("0.0##"); 753 return formatter.format(value.doubleValue()) + " metres"; 754 } 755 756 @Nullable 757 public String getMeteringModeDescription() 758 { 759 // '0' means unknown, '1' average, '2' center weighted average, '3' spot 760 // '4' multi-spot, '5' multi-segment, '6' partial, '255' other 761 Integer value = _directory.getInteger(TAG_METERING_MODE); 762 if (value == null) 763 return null; 764 switch (value) { 765 case 0: return "Unknown"; 766 case 1: return "Average"; 767 case 2: return "Center weighted average"; 768 case 3: return "Spot"; 769 case 4: return "Multi-spot"; 770 case 5: return "Multi-segment"; 771 case 6: return "Partial"; 772 case 255: return "(Other)"; 773 default: 774 return "Unknown (" + value + ")"; 775 } 776 } 777 778 @Nullable 779 public String getWhiteBalanceDescription() 780 { 781 // See http://web.archive.org/web/20131018091152/http://exif.org/Exif2-2.PDF page 35 782 final Integer value = _directory.getInteger(TAG_WHITE_BALANCE); 783 if (value == null) 784 return null; 785 switch (value) { 786 case 0: return "Unknown"; 787 case 1: return "Daylight"; 788 case 2: return "Florescent"; 789 case 3: return "Tungsten"; 790 case 4: return "Flash"; 791 case 9: return "Fine Weather"; 792 case 10: return "Cloudy"; 793 case 11: return "Shade"; 794 case 12: return "Daylight Fluorescent"; 795 case 13: return "Day White Fluorescent"; 796 case 14: return "Cool White Fluorescent"; 797 case 15: return "White Fluorescent"; 798 case 16: return "Warm White Fluorescent"; 799 case 17: return "Standard light"; 800 case 18: return "Standard light (B)"; 801 case 19: return "Standard light (C)"; 802 case 20: return "D55"; 803 case 21: return "D65"; 804 case 22: return "D75"; 805 case 23: return "D50"; 806 case 24: return "Studio Tungsten"; 807 case 255: return "(Other)"; 808 default: 809 return "Unknown (" + value + ")"; 810 } 811 } 812 813 @Nullable 814 public String getFlashDescription() 815 { 816 /* 817 * This is a bit mask. 818 * 0 = flash fired 819 * 1 = return detected 820 * 2 = return able to be detected 821 * 3 = unknown 822 * 4 = auto used 823 * 5 = unknown 824 * 6 = red eye reduction used 825 */ 826 827 final Integer value = _directory.getInteger(TAG_FLASH); 828 829 if (value == null) 830 return null; 831 832 StringBuilder sb = new StringBuilder(); 833 834 if ((value & 0x1) != 0) 835 sb.append("Flash fired"); 836 else 837 sb.append("Flash did not fire"); 838 839 // check if we're able to detect a return, before we mention it 840 if ((value & 0x4) != 0) { 841 if ((value & 0x2) != 0) 842 sb.append(", return detected"); 843 else 844 sb.append(", return not detected"); 845 } 846 847 // If 0x10 is set and the lowest byte is not zero - then flash is Auto 848 if ((value & 0x10) != 0 && (value & 0x0F) != 0) 849 sb.append(", auto"); 850 851 if ((value & 0x40) != 0) 852 sb.append(", red-eye reduction"); 853 854 return sb.toString(); 855 } 856 857 @Nullable 858 public String getFocalLengthDescription() 859 { 860 Rational value = _directory.getRational(TAG_FOCAL_LENGTH); 861 return value == null ? null : getFocalLengthDescription(value.doubleValue()); 862 } 863 864 @Nullable 865 public String getUserCommentDescription() 866 { 867 return getEncodedTextDescription(TAG_USER_COMMENT); 868 } 869 870 @Nullable 871 public String getTemperatureDescription() 872 { 873 Rational value = _directory.getRational(TAG_TEMPERATURE); 874 if (value == null) 875 return null; 876 if (value.getDenominator() == 0xFFFFFFFFL) 877 return "Unknown"; 878 DecimalFormat formatter = new DecimalFormat("0.0"); 879 return formatter.format(value.doubleValue()) + " °C"; 880 } 881 882 @Nullable 883 public String getHumidityDescription() 884 { 885 Rational value = _directory.getRational(TAG_HUMIDITY); 886 if (value == null) 887 return null; 888 if (value.getDenominator() == 0xFFFFFFFFL) 889 return "Unknown"; 890 DecimalFormat formatter = new DecimalFormat("0.0"); 891 return formatter.format(value.doubleValue()) + " %"; 892 } 893 894 @Nullable 895 public String getPressureDescription() 896 { 897 Rational value = _directory.getRational(TAG_PRESSURE); 898 if (value == null) 899 return null; 900 if (value.getDenominator() == 0xFFFFFFFFL) 901 return "Unknown"; 902 DecimalFormat formatter = new DecimalFormat("0.0"); 903 return formatter.format(value.doubleValue()) + " hPa"; 904 } 905 906 @Nullable 907 public String getWaterDepthDescription() 908 { 909 Rational value = _directory.getRational(TAG_WATER_DEPTH); 910 if (value == null) 911 return null; 912 if (value.getDenominator() == 0xFFFFFFFFL) 913 return "Unknown"; 914 DecimalFormat formatter = new DecimalFormat("0.0##"); 915 return formatter.format(value.doubleValue()) + " metres"; 916 } 917 918 @Nullable 919 public String getAccelerationDescription() 920 { 921 Rational value = _directory.getRational(TAG_ACCELERATION); 922 if (value == null) 923 return null; 924 if (value.getDenominator() == 0xFFFFFFFFL) 925 return "Unknown"; 926 DecimalFormat formatter = new DecimalFormat("0.0##"); 927 return formatter.format(value.doubleValue()) + " mGal"; 928 } 929 930 @Nullable 931 public String getCameraElevationAngleDescription() 932 { 933 Rational value = _directory.getRational(TAG_CAMERA_ELEVATION_ANGLE); 934 if (value == null) 935 return null; 936 if (value.getDenominator() == 0xFFFFFFFFL) 937 return "Unknown"; 938 DecimalFormat formatter = new DecimalFormat("0.##"); 939 return formatter.format(value.doubleValue()) + " degrees"; 940 } 941 942 /** The Windows specific tags uses plain Unicode. */ 943 @Nullable 944 private String getUnicodeDescription(int tag) 945 { 946 byte[] bytes = _directory.getByteArray(tag); 947 if (bytes == null) 948 return null; 949 try { 950 // Decode the unicode string and trim the unicode zero "\0" from the end. 951 return new String(bytes, "UTF-16LE").trim(); 952 } catch (UnsupportedEncodingException ex) { 953 return null; 954 } 955 } 956 957 @Nullable 958 public String getWindowsTitleDescription() 959 { 960 return getUnicodeDescription(TAG_WIN_TITLE); 961 } 962 963 @Nullable 964 public String getWindowsCommentDescription() 965 { 966 return getUnicodeDescription(TAG_WIN_COMMENT); 967 } 968 969 @Nullable 970 public String getWindowsAuthorDescription() 971 { 972 return getUnicodeDescription(TAG_WIN_AUTHOR); 973 } 974 975 @Nullable 976 public String getWindowsKeywordsDescription() 977 { 978 return getUnicodeDescription(TAG_WIN_KEYWORDS); 979 } 980 981 @Nullable 982 public String getWindowsSubjectDescription() 983 { 984 return getUnicodeDescription(TAG_WIN_SUBJECT); 985 } 986 987 @Nullable 988 public String getFlashPixVersionDescription() 989 { 990 return getVersionBytesDescription(TAG_FLASHPIX_VERSION, 2); 991 } 992 993 @Nullable 994 public String getColorSpaceDescription() 995 { 996 final Integer value = _directory.getInteger(TAG_COLOR_SPACE); 997 if (value == null) 998 return null; 999 if (value == 1) 1000 return "sRGB"; 1001 if (value == 65535) 1002 return "Undefined"; 1003 return "Unknown (" + value + ")"; 1004 } 1005 1006 @Nullable 1007 public String getExifImageWidthDescription() 1008 { 1009 final Integer value = _directory.getInteger(TAG_EXIF_IMAGE_WIDTH); 1010 return value == null ? null : value + " pixels"; 1011 } 1012 1013 @Nullable 1014 public String getExifImageHeightDescription() 1015 { 1016 final Integer value = _directory.getInteger(TAG_EXIF_IMAGE_HEIGHT); 1017 return value == null ? null : value + " pixels"; 1018 } 1019 1020 @Nullable 1021 public String getFocalPlaneXResolutionDescription() 1022 { 1023 Rational rational = _directory.getRational(TAG_FOCAL_PLANE_X_RESOLUTION); 1024 if (rational == null) 1025 return null; 1026 final String unit = getFocalPlaneResolutionUnitDescription(); 1027 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) 1028 + (unit == null ? "" : " " + unit.toLowerCase()); 1029 } 1030 1031 @Nullable 1032 public String getFocalPlaneYResolutionDescription() 1033 { 1034 Rational rational = _directory.getRational(TAG_FOCAL_PLANE_Y_RESOLUTION); 1035 if (rational == null) 1036 return null; 1037 final String unit = getFocalPlaneResolutionUnitDescription(); 1038 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) 1039 + (unit == null ? "" : " " + unit.toLowerCase()); 1040 } 1041 1042 @Nullable 1043 public String getFocalPlaneResolutionUnitDescription() 1044 { 1045 // Unit of FocalPlaneXResolution/FocalPlaneYResolution. 1046 // '1' means no-unit, '2' inch, '3' centimeter. 1047 return getIndexedDescription(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 1048 1, 1049 "(No unit)", 1050 "Inches", 1051 "cm" 1052 ); 1183 1053 } 1184 1054 … … 1203 1073 1204 1074 @Nullable 1205 public String getComponentConfigurationDescription() 1206 { 1207 int[] components = _directory.getIntArray(TAG_COMPONENTS_CONFIGURATION); 1208 if (components == null) 1209 return null; 1210 String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"}; 1211 StringBuilder componentConfig = new StringBuilder(); 1212 for (int i = 0; i < Math.min(4, components.length); i++) { 1213 int j = components[i]; 1214 if (j > 0 && j < componentStrings.length) { 1215 componentConfig.append(componentStrings[j]); 1075 public String getFileSourceDescription() 1076 { 1077 return getIndexedDescription(TAG_FILE_SOURCE, 1078 1, 1079 "Film Scanner", 1080 "Reflection Print Scanner", 1081 "Digital Still Camera (DSC)" 1082 ); 1083 } 1084 1085 @Nullable 1086 public String getSceneTypeDescription() 1087 { 1088 return getIndexedDescription(TAG_SCENE_TYPE, 1089 1, 1090 "Directly photographed image" 1091 ); 1092 } 1093 1094 /// <summary> 1095 /// String description of CFA Pattern 1096 /// </summary> 1097 /// <remarks> 1098 /// Converted from Exiftool version 10.33 created by Phil Harvey 1099 /// http://www.sno.phy.queensu.ca/~phil/exiftool/ 1100 /// lib\Image\ExifTool\Exif.pm 1101 /// 1102 /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. 1103 /// It does not apply to all sensing methods. 1104 /// </remarks> 1105 @Nullable 1106 public String getCfaPatternDescription() 1107 { 1108 return formatCFAPattern(decodeCfaPattern(TAG_CFA_PATTERN)); 1109 } 1110 1111 /// <summary> 1112 /// Decode raw CFAPattern value 1113 /// </summary> 1114 /// <remarks> 1115 /// Converted from Exiftool version 10.33 created by Phil Harvey 1116 /// http://www.sno.phy.queensu.ca/~phil/exiftool/ 1117 /// lib\Image\ExifTool\Exif.pm 1118 /// 1119 /// The value consists of: 1120 /// - Two short, being the grid width and height of the repeated pattern. 1121 /// - Next, for every pixel in that pattern, an identification code. 1122 /// </remarks> 1123 @Nullable 1124 private int[] decodeCfaPattern(int tagType) 1125 { 1126 int[] ret; 1127 1128 byte[] values = _directory.getByteArray(tagType); 1129 if (values == null) 1130 return null; 1131 1132 if (values.length < 4) 1133 { 1134 ret = new int[values.length]; 1135 for (int i = 0; i < values.length; i++) 1136 ret[i] = values[i]; 1137 return ret; 1138 } 1139 1140 ret = new int[values.length - 2]; 1141 1142 try { 1143 ByteArrayReader reader = new ByteArrayReader(values); 1144 1145 // first two values should be read as 16-bits (2 bytes) 1146 short item0 = reader.getInt16(0); 1147 short item1 = reader.getInt16(2); 1148 1149 Boolean copyArray = false; 1150 int end = 2 + item0 * item1; 1151 if (end > values.length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values 1152 { 1153 // try swapping byte order (I have seen this order different than in EXIF) 1154 reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder()); 1155 item0 = reader.getInt16(0); 1156 item1 = reader.getInt16(2); 1157 1158 if (values.length >= (2 + item0 * item1)) 1159 copyArray = true; 1216 1160 } 1217 } 1218 return componentConfig.toString(); 1219 } 1220 1221 @Nullable 1222 public String getJpegProcDescription() 1223 { 1224 Integer value = _directory.getInteger(TAG_JPEG_PROC); 1225 if (value == null) 1226 return null; 1227 switch (value) { 1228 case 1: return "Baseline"; 1229 case 14: return "Lossless"; 1230 default: 1231 return "Unknown (" + value + ")"; 1232 } 1161 else 1162 copyArray = true; 1163 1164 if(copyArray) 1165 { 1166 ret[0] = item0; 1167 ret[1] = item1; 1168 1169 for (int i = 4; i < values.length; i++) 1170 ret[i - 2] = reader.getInt8(i); 1171 } 1172 } catch (IOException ex) { 1173 _directory.addError("IO exception processing data: " + ex.getMessage()); 1174 } 1175 1176 return ret; 1177 } 1178 1179 @Nullable 1180 public String getCustomRenderedDescription() 1181 { 1182 return getIndexedDescription(TAG_CUSTOM_RENDERED, 1183 "Normal process", 1184 "Custom process" 1185 ); 1186 } 1187 1188 @Nullable 1189 public String getExposureModeDescription() 1190 { 1191 return getIndexedDescription(TAG_EXPOSURE_MODE, 1192 "Auto exposure", 1193 "Manual exposure", 1194 "Auto bracket" 1195 ); 1196 } 1197 1198 @Nullable 1199 public String getWhiteBalanceModeDescription() 1200 { 1201 return getIndexedDescription(TAG_WHITE_BALANCE_MODE, 1202 "Auto white balance", 1203 "Manual white balance" 1204 ); 1205 } 1206 1207 @Nullable 1208 public String getDigitalZoomRatioDescription() 1209 { 1210 Rational value = _directory.getRational(TAG_DIGITAL_ZOOM_RATIO); 1211 return value == null 1212 ? null 1213 : value.getNumerator() == 0 1214 ? "Digital zoom not used" 1215 : new DecimalFormat("0.#").format(value.doubleValue()); 1216 } 1217 1218 @Nullable 1219 public String get35mmFilmEquivFocalLengthDescription() 1220 { 1221 Integer value = _directory.getInteger(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH); 1222 return value == null 1223 ? null 1224 : value == 0 1225 ? "Unknown" 1226 : getFocalLengthDescription(value); 1227 } 1228 1229 @Nullable 1230 public String getSceneCaptureTypeDescription() 1231 { 1232 return getIndexedDescription(TAG_SCENE_CAPTURE_TYPE, 1233 "Standard", 1234 "Landscape", 1235 "Portrait", 1236 "Night scene" 1237 ); 1238 } 1239 1240 @Nullable 1241 public String getGainControlDescription() 1242 { 1243 return getIndexedDescription(TAG_GAIN_CONTROL, 1244 "None", 1245 "Low gain up", 1246 "Low gain down", 1247 "High gain up", 1248 "High gain down" 1249 ); 1250 } 1251 1252 @Nullable 1253 public String getContrastDescription() 1254 { 1255 return getIndexedDescription(TAG_CONTRAST, 1256 "None", 1257 "Soft", 1258 "Hard" 1259 ); 1260 } 1261 1262 @Nullable 1263 public String getSaturationDescription() 1264 { 1265 return getIndexedDescription(TAG_SATURATION, 1266 "None", 1267 "Low saturation", 1268 "High saturation" 1269 ); 1270 } 1271 1272 @Nullable 1273 public String getSharpnessDescription() 1274 { 1275 return getIndexedDescription(TAG_SHARPNESS, 1276 "None", 1277 "Low", 1278 "Hard" 1279 ); 1280 } 1281 1282 @Nullable 1283 public String getSubjectDistanceRangeDescription() 1284 { 1285 return getIndexedDescription(TAG_SUBJECT_DISTANCE_RANGE, 1286 "Unknown", 1287 "Macro", 1288 "Close view", 1289 "Distant view" 1290 ); 1291 } 1292 1293 @Nullable 1294 public String getLensSpecificationDescription() 1295 { 1296 return getLensSpecificationDescription(TAG_LENS_SPECIFICATION); 1233 1297 } 1234 1298 } -
trunk/src/com/drew/metadata/exif/ExifDirectoryBase.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 189 189 public static final int TAG_FNUMBER = 0x829D; 190 190 public static final int TAG_IPTC_NAA = 0x83BB; 191 public static final int TAG_PHOTOSHOP_SETTINGS = 0x8649; 191 192 public static final int TAG_INTER_COLOR_PROFILE = 0x8773; 192 193 /** … … 230 231 public static final int TAG_STANDARD_OUTPUT_SENSITIVITY = 0x8831; 231 232 public static final int TAG_RECOMMENDED_EXPOSURE_INDEX = 0x8832; 232 /** Non-standard, but in use. */233 public static final int TAG_ TIME_ZONE_OFFSET = 0x882A;234 public static final int TAG_ SELF_TIMER_MODE = 0x882B;233 public static final int TAG_ISO_SPEED = 0x8833; 234 public static final int TAG_ISO_SPEED_LATITUDE_YYY = 0x8834; 235 public static final int TAG_ISO_SPEED_LATITUDE_ZZZ = 0x8835; 235 236 236 237 public static final int TAG_EXIF_VERSION = 0x9000; 237 238 public static final int TAG_DATETIME_ORIGINAL = 0x9003; 238 239 public static final int TAG_DATETIME_DIGITIZED = 0x9004; 240 public static final int TAG_OFFSET_TIME = 0x9010; 241 public static final int TAG_OFFSET_TIME_ORIGINAL = 0x9011; 242 public static final int TAG_OFFSET_TIME_DIGITIZED = 0x9012; 239 243 240 244 public static final int TAG_COMPONENTS_CONFIGURATION = 0x9101; … … 357 361 public static final int TAG_SUBSECOND_TIME_ORIGINAL = 0x9291; 358 362 public static final int TAG_SUBSECOND_TIME_DIGITIZED = 0x9292; 363 364 public static final int TAG_TEMPERATURE = 0x9400; 365 public static final int TAG_HUMIDITY = 0x9401; 366 public static final int TAG_PRESSURE = 0x9402; 367 public static final int TAG_WATER_DEPTH = 0x9403; 368 public static final int TAG_ACCELERATION = 0x9404; 369 public static final int TAG_CAMERA_ELEVATION_ANGLE = 0x9405; 359 370 360 371 /** The image title, as used by Windows XP. */ … … 671 682 map.put(TAG_FNUMBER, "F-Number"); 672 683 map.put(TAG_IPTC_NAA, "IPTC/NAA"); 684 map.put(TAG_PHOTOSHOP_SETTINGS, "Photoshop Settings"); 673 685 map.put(TAG_INTER_COLOR_PROFILE, "Inter Color Profile"); 674 686 map.put(TAG_EXPOSURE_PROGRAM, "Exposure Program"); … … 682 694 map.put(TAG_STANDARD_OUTPUT_SENSITIVITY, "Standard Output Sensitivity"); 683 695 map.put(TAG_RECOMMENDED_EXPOSURE_INDEX, "Recommended Exposure Index"); 684 map.put(TAG_TIME_ZONE_OFFSET, "Time Zone Offset"); 685 map.put(TAG_SELF_TIMER_MODE, "Self Timer Mode"); 696 map.put(TAG_ISO_SPEED, "ISO Speed"); 697 map.put(TAG_ISO_SPEED_LATITUDE_YYY, "ISO Speed Latitude yyy"); 698 map.put(TAG_ISO_SPEED_LATITUDE_ZZZ, "ISO Speed Latitude zzz"); 686 699 map.put(TAG_EXIF_VERSION, "Exif Version"); 687 700 map.put(TAG_DATETIME_ORIGINAL, "Date/Time Original"); 688 701 map.put(TAG_DATETIME_DIGITIZED, "Date/Time Digitized"); 702 map.put(TAG_OFFSET_TIME, "Offset Time"); 703 map.put(TAG_OFFSET_TIME_ORIGINAL, "Offset Time Original"); 704 map.put(TAG_OFFSET_TIME_DIGITIZED, "Offset Time Digitized"); 689 705 map.put(TAG_COMPONENTS_CONFIGURATION, "Components Configuration"); 690 706 map.put(TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL, "Compressed Bits Per Pixel"); … … 715 731 map.put(TAG_SUBSECOND_TIME_ORIGINAL, "Sub-Sec Time Original"); 716 732 map.put(TAG_SUBSECOND_TIME_DIGITIZED, "Sub-Sec Time Digitized"); 733 map.put(TAG_TEMPERATURE, "Temperature"); 734 map.put(TAG_HUMIDITY, "Humidity"); 735 map.put(TAG_PRESSURE, "Pressure"); 736 map.put(TAG_WATER_DEPTH, "Water Depth"); 737 map.put(TAG_ACCELERATION, "Acceleration"); 738 map.put(TAG_CAMERA_ELEVATION_ANGLE, "Camera Elevation Angle"); 717 739 map.put(TAG_WIN_TITLE, "Windows XP Title"); 718 740 map.put(TAG_WIN_COMMENT, "Windows XP Comment"); -
trunk/src/com/drew/metadata/exif/ExifIFD0Descriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifIFD0Directory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 22 22 package com.drew.metadata.exif; 23 23 24 import com.drew.lang.annotations.NotNull; 25 24 26 import java.util.HashMap; 25 26 import com.drew.lang.annotations.NotNull;27 27 28 28 /** -
trunk/src/com/drew/metadata/exif/ExifImageDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifImageDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifInteropDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifInteropDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifSubIFDDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifSubIFDDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 23 23 import com.drew.lang.annotations.NotNull; 24 24 import com.drew.lang.annotations.Nullable; 25 import com.drew.metadata.Directory; 25 26 26 27 import java.util.Date; … … 67 68 68 69 /** 69 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds 70 * representing the date and time when this image was captured. Attempts will be made to parse the 71 * values as though it is in the GMT {@link TimeZone}. 70 * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date 71 * object with milliseconds representing the date and time when this image was modified. If 72 * the time offset tag does not exist, attempts will be made to parse the values as though it is 73 * in the GMT {@link TimeZone}. 74 * 75 * @return A Date object representing when this image was modified, if possible, otherwise null 76 */ 77 @Nullable 78 public Date getDateModified() 79 { 80 return getDateModified(null); 81 } 82 83 /** 84 * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date 85 * object with milliseconds representing the date and time when this image was modified. If 86 * the time offset tag does not exist, attempts will be made to parse the values as though it is 87 * in the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). 88 * 89 * @param timeZone the time zone to use 90 * @return A Date object representing when this image was modified, if possible, otherwise null 91 */ 92 @Nullable 93 public Date getDateModified(@Nullable TimeZone timeZone) 94 { 95 Directory parent = getParent(); 96 if (parent instanceof ExifIFD0Directory) { 97 TimeZone timeZoneModified = getTimeZone(TAG_OFFSET_TIME); 98 return parent.getDate(TAG_DATETIME, getString(TAG_SUBSECOND_TIME), 99 (timeZoneModified != null) ? timeZoneModified : timeZone); 100 } else { 101 return null; 102 } 103 } 104 105 /** 106 * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date 107 * object with milliseconds representing the date and time when this image was captured. If 108 * the time offset tag does not exist, attempts will be made to parse the values as though it is 109 * in the GMT {@link TimeZone}. 72 110 * 73 111 * @return A Date object representing when this image was captured, if possible, otherwise null … … 80 118 81 119 /** 82 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds83 * representing the date and time when this image was captured. Attempts will be made to parse the84 * values as though it is in the {@link TimeZone} represented by the {@code timeZone} parameter85 * (if it is non-null).120 * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date 121 * object with milliseconds representing the date and time when this image was captured. If 122 * the time offset tag does not exist, attempts will be made to parse the values as though it is 123 * in the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). 86 124 * 87 125 * @param timeZone the time zone to use … … 91 129 public Date getDateOriginal(@Nullable TimeZone timeZone) 92 130 { 93 return getDate(TAG_DATETIME_ORIGINAL, getString(TAG_SUBSECOND_TIME_ORIGINAL), timeZone); 131 TimeZone timeZoneOriginal = getTimeZone(TAG_OFFSET_TIME_ORIGINAL); 132 return getDate(TAG_DATETIME_ORIGINAL, getString(TAG_SUBSECOND_TIME_ORIGINAL), 133 (timeZoneOriginal != null) ? timeZoneOriginal : timeZone); 94 134 } 95 135 96 136 /** 97 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds 98 * representing the date and time when this image was digitized. Attempts will be made to parse the 99 * values as though it is in the GMT {@link TimeZone}. 137 * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date 138 * object with milliseconds representing the date and time when this image was digitized. If 139 * the time offset tag does not exist, attempts will be made to parse the values as though it is 140 * in the GMT {@link TimeZone}. 100 141 * 101 142 * @return A Date object representing when this image was digitized, if possible, otherwise null … … 108 149 109 150 /** 110 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds111 * representing the date and time when this image was digitized. Attempts will be made to parse the112 * values as though it is in the {@link TimeZone} represented by the {@code timeZone} parameter113 * (if it is non-null).151 * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date 152 * object with milliseconds representing the date and time when this image was digitized. If 153 * the time offset tag does not exist, attempts will be made to parse the values as though it is 154 * in the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). 114 155 * 115 156 * @param timeZone the time zone to use … … 119 160 public Date getDateDigitized(@Nullable TimeZone timeZone) 120 161 { 121 return getDate(TAG_DATETIME_DIGITIZED, getString(TAG_SUBSECOND_TIME_DIGITIZED), timeZone); 162 TimeZone timeZoneDigitized = getTimeZone(TAG_OFFSET_TIME_DIGITIZED); 163 return getDate(TAG_DATETIME_DIGITIZED, getString(TAG_SUBSECOND_TIME_DIGITIZED), 164 (timeZoneDigitized != null) ? timeZoneDigitized : timeZone); 165 } 166 167 @Nullable 168 private TimeZone getTimeZone(int tagType) 169 { 170 String timeOffset = getString(tagType); 171 if (timeOffset != null && timeOffset.matches("[\\+\\-]\\d\\d:\\d\\d")) { 172 return TimeZone.getTimeZone("GMT" + timeOffset); 173 } else { 174 return null; 175 } 122 176 } 123 177 } -
trunk/src/com/drew/metadata/exif/ExifThumbnailDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 22 22 package com.drew.metadata.exif; 23 23 24 import static com.drew.metadata.exif.ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH;25 import static com.drew.metadata.exif.ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET;26 27 24 import com.drew.lang.annotations.NotNull; 28 25 import com.drew.lang.annotations.Nullable; 26 27 import static com.drew.metadata.exif.ExifThumbnailDirectory.*; 29 28 30 29 /** -
trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifTiffHandler.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 30 30 import com.drew.imaging.tiff.TiffProcessingException; 31 31 import com.drew.imaging.tiff.TiffReader; 32 import com.drew.lang.BufferBoundsException; 32 33 import com.drew.lang.Charsets; 33 34 import com.drew.lang.RandomAccessReader; … … 38 39 import com.drew.metadata.Metadata; 39 40 import com.drew.metadata.StringValue; 40 import com.drew.metadata.exif.makernotes.*; 41 import com.drew.metadata.exif.makernotes.AppleMakernoteDirectory; 42 import com.drew.metadata.exif.makernotes.CanonMakernoteDirectory; 43 import com.drew.metadata.exif.makernotes.CasioType1MakernoteDirectory; 44 import com.drew.metadata.exif.makernotes.CasioType2MakernoteDirectory; 45 import com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory; 46 import com.drew.metadata.exif.makernotes.KodakMakernoteDirectory; 47 import com.drew.metadata.exif.makernotes.KyoceraMakernoteDirectory; 48 import com.drew.metadata.exif.makernotes.LeicaMakernoteDirectory; 49 import com.drew.metadata.exif.makernotes.LeicaType5MakernoteDirectory; 50 import com.drew.metadata.exif.makernotes.NikonType1MakernoteDirectory; 51 import com.drew.metadata.exif.makernotes.NikonType2MakernoteDirectory; 52 import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory; 53 import com.drew.metadata.exif.makernotes.OlympusEquipmentMakernoteDirectory; 54 import com.drew.metadata.exif.makernotes.OlympusFocusInfoMakernoteDirectory; 55 import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory; 56 import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory; 57 import com.drew.metadata.exif.makernotes.OlympusRawDevelopment2MakernoteDirectory; 58 import com.drew.metadata.exif.makernotes.OlympusRawDevelopmentMakernoteDirectory; 59 import com.drew.metadata.exif.makernotes.OlympusRawInfoMakernoteDirectory; 60 import com.drew.metadata.exif.makernotes.PanasonicMakernoteDirectory; 61 import com.drew.metadata.exif.makernotes.PentaxMakernoteDirectory; 62 import com.drew.metadata.exif.makernotes.ReconyxHyperFireMakernoteDirectory; 63 import com.drew.metadata.exif.makernotes.ReconyxUltraFireMakernoteDirectory; 64 import com.drew.metadata.exif.makernotes.RicohMakernoteDirectory; 65 import com.drew.metadata.exif.makernotes.SamsungType2MakernoteDirectory; 66 import com.drew.metadata.exif.makernotes.SanyoMakernoteDirectory; 67 import com.drew.metadata.exif.makernotes.SigmaMakernoteDirectory; 68 import com.drew.metadata.exif.makernotes.SonyType1MakernoteDirectory; 69 import com.drew.metadata.exif.makernotes.SonyType6MakernoteDirectory; 41 70 import com.drew.metadata.iptc.IptcReader; 42 71 import com.drew.metadata.tiff.DirectoryTiffHandler; … … 54 83 public ExifTiffHandler(@NotNull Metadata metadata, @Nullable Directory parentDirectory) 55 84 { 56 super(metadata); 57 58 if (parentDirectory != null) 59 _currentDirectory.setParent(parentDirectory); 85 super(metadata, parentDirectory); 60 86 } 61 87 … … 67 93 final int panasonicRawTiffMarker = 0x0055; // for RW2 files 68 94 69 switch (marker) 70 { 95 switch (marker) { 71 96 case standardTiffMarker: 72 case olympusRawTiffMarker: // T odo:implement an IFD0, if there is one73 case olympusRawTiffMarker2: // T odo:implement an IFD0, if there is one97 case olympusRawTiffMarker: // TODO implement an IFD0, if there is one 98 case olympusRawTiffMarker2: // TODO implement an IFD0, if there is one 74 99 pushDirectory(ExifIFD0Directory.class); 75 100 break; … … 172 197 173 198 // an unknown (0) formatCode needs to be potentially handled later as a highly custom directory tag 174 if (formatCode == 0)199 if (formatCode == 0) 175 200 return 0L; 176 201 … … 185 210 final int byteCount) throws IOException 186 211 { 212 assert(_currentDirectory != null); 213 187 214 // Some 0x0000 tags have a 0 byteCount. Determine whether it's bad. 188 if (tagId == 0) 189 { 190 if (_currentDirectory.containsTag(tagId)) 191 { 215 if (tagId == 0) { 216 if (_currentDirectory.containsTag(tagId)) { 192 217 // Let it go through for now. Some directories handle it, some don't 193 218 return false; … … 215 240 } 216 241 217 if ( HandlePrintIM(_currentDirectory, tagId))242 if (handlePrintIM(_currentDirectory, tagId)) 218 243 { 219 244 PrintIMDirectory printIMDirectory = new PrintIMDirectory(); 220 245 printIMDirectory.setParent(_currentDirectory); 221 246 _metadata.addDirectory(printIMDirectory); 222 ProcessPrintIM(printIMDirectory, tagOffset, reader, byteCount);247 processPrintIM(printIMDirectory, tagOffset, reader, byteCount); 223 248 return true; 224 249 } … … 226 251 // Note: these also appear in tryEnterSubIfd because some are IFD pointers while others begin immediately 227 252 // for the same directories 228 if(_currentDirectory instanceof OlympusMakernoteDirectory) 229 { 230 switch (tagId) 231 { 253 if (_currentDirectory instanceof OlympusMakernoteDirectory) { 254 switch (tagId) { 232 255 case OlympusMakernoteDirectory.TAG_EQUIPMENT: 233 256 pushDirectory(OlympusEquipmentMakernoteDirectory.class); … … 265 288 } 266 289 267 if (_currentDirectory instanceof PanasonicRawIFD0Directory) 268 { 290 if (_currentDirectory instanceof PanasonicRawIFD0Directory) { 269 291 // these contain binary data with specific offsets, and can't be processed as regular ifd's. 270 292 // The binary data is broken into 'fake' tags and there is a pattern. 271 switch (tagId) 272 { 293 switch (tagId) { 273 294 case PanasonicRawIFD0Directory.TagWbInfo: 274 295 PanasonicRawWbInfoDirectory dirWbInfo = new PanasonicRawWbInfoDirectory(); 275 296 dirWbInfo.setParent(_currentDirectory); 276 297 _metadata.addDirectory(dirWbInfo); 277 ProcessBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2);298 processBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2); 278 299 return true; 279 300 case PanasonicRawIFD0Directory.TagWbInfo2: … … 281 302 dirWbInfo2.setParent(_currentDirectory); 282 303 _metadata.addDirectory(dirWbInfo2); 283 ProcessBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3);304 processBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3); 284 305 return true; 285 306 case PanasonicRawIFD0Directory.TagDistortionInfo: … … 287 308 dirDistort.setParent(_currentDirectory); 288 309 _metadata.addDirectory(dirDistort); 289 ProcessBinary(dirDistort, tagOffset, reader, byteCount, true, 1);310 processBinary(dirDistort, tagOffset, reader, byteCount, true, 1); 290 311 return true; 291 312 } … … 293 314 294 315 // Panasonic RAW sometimes contains an embedded version of the data as a JPG file. 295 if (tagId == PanasonicRawIFD0Directory.TagJpgFromRaw && _currentDirectory instanceof PanasonicRawIFD0Directory) 296 { 316 if (tagId == PanasonicRawIFD0Directory.TagJpgFromRaw && _currentDirectory instanceof PanasonicRawIFD0Directory) { 297 317 byte[] jpegrawbytes = reader.getBytes(tagOffset, byteCount); 298 318 … … 316 336 } 317 337 318 private static void ProcessBinary(@NotNull final Directory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount, final Boolean issigned, final int arrayLength) throws IOException338 private static void processBinary(@NotNull final Directory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount, final Boolean isSigned, final int arrayLength) throws IOException 319 339 { 320 340 // expects signed/unsigned int16 (for now) 321 //int byteSize = is signed ? sizeof(short) : sizeof(ushort);341 //int byteSize = isSigned ? sizeof(short) : sizeof(ushort); 322 342 int byteSize = 2; 323 343 324 344 // 'directory' is assumed to contain tags that correspond to the byte position unless it's a set of bytes 325 for (int i = 0; i < byteCount; i++) 326 { 327 if (directory.hasTagName(i)) 328 { 345 for (int i = 0; i < byteCount; i++) { 346 if (directory.hasTagName(i)) { 329 347 // only process this tag if the 'next' integral tag exists. Otherwise, it's a set of bytes 330 if (i < byteCount - 1 && directory.hasTagName(i + 1)) 331 { 332 if(issigned) 348 if (i < byteCount - 1 && directory.hasTagName(i + 1)) { 349 if (isSigned) 333 350 directory.setObject(i, reader.getInt16(tagValueOffset + (i* byteSize))); 334 351 else 335 352 directory.setObject(i, reader.getUInt16(tagValueOffset + (i* byteSize))); 336 } 337 else 338 { 353 } else { 339 354 // the next arrayLength bytes are a multi-byte value 340 if (issigned) 341 { 355 if (isSigned) { 342 356 short[] val = new short[arrayLength]; 343 357 for (int j = 0; j<val.length; j++) 344 358 val[j] = reader.getInt16(tagValueOffset + ((i + j) * byteSize)); 345 359 directory.setObjectArray(i, val); 346 } 347 else 348 { 360 } else { 349 361 int[] val = new int[arrayLength]; 350 362 for (int j = 0; j<val.length; j++) … … 359 371 } 360 372 373 /** Read a given number of bytes from the stream 374 * 375 * This method is employed to "suppress" attempts to read beyond end of the 376 * file as may happen at the beginning of processMakernote when we read 377 * increasingly longer camera makes. 378 * 379 * Instead of failing altogether in this context we return an empty string 380 * which will fail all sensible attempts to compare to makes while avoiding 381 * a full-on failure. 382 */ 383 @NotNull 384 private static String getReaderString(final @NotNull RandomAccessReader reader, final int makernoteOffset, final int bytesRequested) throws IOException 385 { 386 try { 387 return reader.getString(makernoteOffset, bytesRequested, Charsets.UTF_8); 388 } catch(BufferBoundsException e) { 389 return ""; 390 } 391 } 392 361 393 private boolean processMakernote(final int makernoteOffset, 362 394 final @NotNull Set<Integer> processedIfdOffsets, … … 364 396 final @NotNull RandomAccessReader reader) throws IOException 365 397 { 398 assert(_currentDirectory != null); 399 366 400 // Determine the camera model and makernote format. 367 401 Directory ifd0Directory = _metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); … … 369 403 String cameraMake = ifd0Directory == null ? null : ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE); 370 404 371 final String firstTwoChars = reader.getString(makernoteOffset, 2, Charsets.UTF_8);372 final String firstThreeChars = reader.getString(makernoteOffset, 3, Charsets.UTF_8);373 final String firstFourChars = reader.getString(makernoteOffset, 4, Charsets.UTF_8);374 final String firstFiveChars = reader.getString(makernoteOffset, 5, Charsets.UTF_8);375 final String firstSixChars = reader.getString(makernoteOffset, 6, Charsets.UTF_8);376 final String firstSevenChars = reader.getString(makernoteOffset, 7, Charsets.UTF_8);377 final String firstEightChars = reader.getString(makernoteOffset, 8, Charsets.UTF_8);378 final String firstNineChars = reader.getString(makernoteOffset, 9, Charsets.UTF_8);379 final String firstTenChars = reader.getString(makernoteOffset, 10, Charsets.UTF_8);380 final String firstTwelveChars = reader.getString(makernoteOffset, 12, Charsets.UTF_8);405 final String firstTwoChars = getReaderString(reader, makernoteOffset, 2); 406 final String firstThreeChars = getReaderString(reader, makernoteOffset, 3); 407 final String firstFourChars = getReaderString(reader, makernoteOffset, 4); 408 final String firstFiveChars = getReaderString(reader, makernoteOffset, 5); 409 final String firstSixChars = getReaderString(reader, makernoteOffset, 6); 410 final String firstSevenChars = getReaderString(reader, makernoteOffset, 7); 411 final String firstEightChars = getReaderString(reader, makernoteOffset, 8); 412 final String firstNineChars = getReaderString(reader, makernoteOffset, 9); 413 final String firstTenChars = getReaderString(reader, makernoteOffset, 10); 414 final String firstTwelveChars = getReaderString(reader, makernoteOffset, 12); 381 415 382 416 boolean byteOrderBefore = reader.isMotorolaByteOrder(); … … 501 535 return false; 502 536 } 503 } else if ("Panasonic\u0000\u0000\u0000".equals( reader.getString(makernoteOffset, 12, Charsets.UTF_8))) {537 } else if ("Panasonic\u0000\u0000\u0000".equals(firstTwelveChars)) { 504 538 // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD 505 539 // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment … … 575 609 } 576 610 577 private static Boolean HandlePrintIM(@NotNull final Directory directory, final int tagId)611 private static boolean handlePrintIM(@NotNull final Directory directory, final int tagId) 578 612 { 579 613 if (tagId == ExifDirectoryBase.TAG_PRINT_IMAGE_MATCHING_INFO) 580 614 return true; 581 615 582 if (tagId == 0x0E00) 583 { 616 if (tagId == 0x0E00) { 584 617 // Tempting to say every tagid of 0x0E00 is a PIM tag, but can't be 100% sure 585 618 if (directory instanceof CasioType2MakernoteDirectory || … … 606 639 /// lib\Image\ExifTool\PrintIM.pm 607 640 /// </remarks> 608 private static void ProcessPrintIM(@NotNull final PrintIMDirectory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount) throws IOException641 private static void processPrintIM(@NotNull final PrintIMDirectory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount) throws IOException 609 642 { 610 643 Boolean resetByteOrder = null; 611 644 612 if (byteCount == 0) 613 { 645 if (byteCount == 0) { 614 646 directory.addError("Empty PrintIM data"); 615 647 return; 616 648 } 617 649 618 if (byteCount <= 15) 619 { 650 if (byteCount <= 15) { 620 651 directory.addError("Bad PrintIM data"); 621 652 return; … … 624 655 String header = reader.getString(tagValueOffset, 12, Charsets.UTF_8); 625 656 626 if (!header.startsWith("PrintIM")) //, StringComparison.Ordinal)) 627 { 657 if (!header.startsWith("PrintIM")) { 628 658 directory.addError("Invalid PrintIM header"); 629 659 return; … … 632 662 // check size of PrintIM block 633 663 int num = reader.getUInt16(tagValueOffset + 14); 634 if (byteCount < 16 + num * 6) 635 {664 665 if (byteCount < 16 + num * 6) { 636 666 // size is too big, maybe byte ordering is wrong 637 667 resetByteOrder = reader.isMotorolaByteOrder(); 638 668 reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder()); 639 669 num = reader.getUInt16(tagValueOffset + 14); 640 if (byteCount < 16 + num * 6) 641 { 670 if (byteCount < 16 + num * 6) { 642 671 directory.addError("Bad PrintIM size"); 643 672 return; … … 647 676 directory.setObject(PrintIMDirectory.TagPrintImVersion, header.substring(8, 12)); 648 677 649 for (int n = 0; n < num; n++) 650 { 678 for (int n = 0; n < num; n++) { 651 679 int pos = tagValueOffset + 16 + n * 6; 652 680 int tag = reader.getUInt16(pos); … … 712 740 build = null; 713 741 } 714 if (build != null) 715 {742 743 if (build != null) { 716 744 directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d.%s", major, minor, revision, build)); 717 } 718 else 719 { 745 } else { 720 746 directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d", major, minor, revision)); 721 747 directory.addError("Error processing Reconyx HyperFire makernote data: build '" + buildYearAndDate + "' is not in the expected format and will be omitted from Firmware Version."); … … 746 772 (month >= 1 && month < 13) && 747 773 (day >= 1 && day < 32) && 748 (year >= 1 && year <= 9999)) 749 { 774 (year >= 1 && year <= 9999)) { 750 775 directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL, 751 776 String.format("%4d:%2d:%2d %2d:%2d:%2d", year, month, day, hour, minutes, seconds)); 752 } 753 else 754 { 777 } else { 755 778 directory.addError("Error processing Reconyx HyperFire makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time."); 756 779 } … … 778 801 /*uint makernoteID = ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernoteID)); 779 802 directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernoteID, makernoteID); 780 if (makernoteID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_ID) 781 { 803 if (makernoteID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_ID) { 782 804 directory.addError("Error processing Reconyx UltraFire makernote data: unknown Makernote ID 0x" + makernoteID.ToString("x8")); 783 805 return; … … 786 808 uint makernotePublicID = ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernotePublicID)); 787 809 directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernotePublicID, makernotePublicID); 788 if (makernotePublicID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_PUBLIC_ID) 789 { 810 if (makernotePublicID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_PUBLIC_ID) { 790 811 directory.addError("Error processing Reconyx UltraFire makernote data: unknown Makernote Public ID 0x" + makernotePublicID.ToString("x8")); 791 812 return; … … 818 839 (month >= 1 && month < 13) && 819 840 (day >= 1 && day < 32) && 820 (year >= 1 && year <= 9999)) 821 { 841 (year >= 1 && year <= 9999)) { 822 842 directory.Set(ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL, new DateTime(year, month, day, hour, minutes, seconds, DateTimeKind.Unspecified)); 823 } 824 else 825 { 843 } else { 826 844 directory.addError("Error processing Reconyx UltraFire makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time."); 827 845 }*/ -
trunk/src/com/drew/metadata/exif/GpsDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 59 59 case TAG_MEASURE_MODE: 60 60 return getGpsMeasureModeDescription(); 61 case TAG_DOP: 62 return getGpsDopDescription(); 61 63 case TAG_SPEED_REF: 62 64 return getGpsSpeedRefDescription(); 65 case TAG_SPEED: 66 return getGpsSpeedDescription(); 63 67 case TAG_TRACK_REF: 64 68 case TAG_IMG_DIRECTION_REF: … … 69 73 case TAG_DEST_BEARING: 70 74 return getGpsDirectionDescription(tagType); 75 case TAG_DEST_LATITUDE: 76 return getGpsDestLatitudeDescription(); 77 case TAG_DEST_LONGITUDE: 78 return getGpsDestLongitudeDescription(); 71 79 case TAG_DEST_DISTANCE_REF: 72 80 return getGpsDestinationReferenceDescription(); 81 case TAG_DEST_DISTANCE: 82 return getGpsDestDistanceDescription(); 73 83 case TAG_TIME_STAMP: 74 84 return getGpsTimeStampDescription(); … … 79 89 // three rational numbers -- displayed in HH"MM"SS.ss 80 90 return getGpsLatitudeDescription(); 91 case TAG_PROCESSING_METHOD: 92 return getGpsProcessingMethodDescription(); 93 case TAG_AREA_INFORMATION: 94 return getGpsAreaInformationDescription(); 81 95 case TAG_DIFFERENTIAL: 82 96 return getGpsDifferentialDescription(); 97 case TAG_H_POSITIONING_ERROR: 98 return getGpsHPositioningErrorDescription(); 83 99 default: 84 100 return super.getDescription(tagType); … … 121 137 122 138 @Nullable 139 public String getGpsDestLatitudeDescription() 140 { 141 Rational[] latitudes = _directory.getRationalArray(TAG_DEST_LATITUDE); 142 String latitudeRef = _directory.getString(TAG_DEST_LATITUDE_REF); 143 144 if (latitudes == null || latitudes.length != 3 || latitudeRef == null) 145 return null; 146 147 Double lat = GeoLocation.degreesMinutesSecondsToDecimal( 148 latitudes[0], latitudes[1], latitudes[2], latitudeRef.equalsIgnoreCase("S")); 149 150 return lat == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(lat); 151 } 152 153 @Nullable 154 public String getGpsDestLongitudeDescription() 155 { 156 Rational[] longitudes = _directory.getRationalArray(TAG_LONGITUDE); 157 String longitudeRef = _directory.getString(TAG_LONGITUDE_REF); 158 159 if (longitudes == null || longitudes.length != 3 || longitudeRef == null) 160 return null; 161 162 Double lon = GeoLocation.degreesMinutesSecondsToDecimal( 163 longitudes[0], longitudes[1], longitudes[2], longitudeRef.equalsIgnoreCase("W")); 164 165 return lon == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(lon); 166 } 167 168 @Nullable 123 169 public String getGpsDestinationReferenceDescription() 124 170 { … … 139 185 140 186 @Nullable 187 public String getGpsDestDistanceDescription() 188 { 189 final Rational value = _directory.getRational(TAG_DEST_DISTANCE); 190 if (value == null) 191 return null; 192 final String unit = getGpsDestinationReferenceDescription(); 193 return String.format("%s %s", 194 new DecimalFormat("0.##").format(value.doubleValue()), 195 unit == null ? "unit" : unit.toLowerCase()); 196 } 197 198 @Nullable 141 199 public String getGpsDirectionDescription(int tagType) 142 200 { … … 166 224 167 225 @Nullable 226 public String getGpsDopDescription() 227 { 228 final Rational value = _directory.getRational(TAG_DOP); 229 return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()); 230 } 231 232 @Nullable 168 233 public String getGpsSpeedRefDescription() 169 234 { … … 173 238 String gpsSpeedRef = value.trim(); 174 239 if ("K".equalsIgnoreCase(gpsSpeedRef)) { 175 return "k ph";240 return "km/h"; 176 241 } else if ("M".equalsIgnoreCase(gpsSpeedRef)) { 177 242 return "mph"; … … 181 246 return "Unknown (" + gpsSpeedRef + ")"; 182 247 } 248 } 249 250 @Nullable 251 public String getGpsSpeedDescription() 252 { 253 final Rational value = _directory.getRational(TAG_SPEED); 254 if (value == null) 255 return null; 256 final String unit = getGpsSpeedRefDescription(); 257 return String.format("%s %s", 258 new DecimalFormat("0.##").format(value.doubleValue()), 259 unit == null ? "unit" : unit.toLowerCase()); 183 260 } 184 261 … … 225 302 { 226 303 final Rational value = _directory.getRational(TAG_ALTITUDE); 227 return value == null ? null : value.intValue() + " metres"; 304 return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()) + " metres"; 305 } 306 307 @Nullable 308 public String getGpsProcessingMethodDescription() 309 { 310 return getEncodedTextDescription(TAG_PROCESSING_METHOD); 311 } 312 313 @Nullable 314 public String getGpsAreaInformationDescription() 315 { 316 return getEncodedTextDescription(TAG_AREA_INFORMATION); 228 317 } 229 318 … … 232 321 { 233 322 return getIndexedDescription(TAG_DIFFERENTIAL, "No Correction", "Differential Corrected"); 323 } 324 325 @Nullable 326 public String getGpsHPositioningErrorDescription() 327 { 328 final Rational value = _directory.getRational(TAG_H_POSITIONING_ERROR); 329 return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()) + " metres"; 234 330 } 235 331 -
trunk/src/com/drew/metadata/exif/GpsDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 95 95 /** Distance to destination GPSDestDistance 26 1A RATIONAL 1 */ 96 96 public static final int TAG_DEST_DISTANCE = 0x001A; 97 98 /** Values of "GPS", "CELLID", "WLAN" or "MANUAL" by the EXIF spec. */ 97 /** Name of the method used for location finding GPSProcessingMethod 27 1B UNDEFINED Any */ 99 98 public static final int TAG_PROCESSING_METHOD = 0x001B; 99 /** Name of the GPS area GPSAreaInformation 28 1C UNDEFINED Any */ 100 100 public static final int TAG_AREA_INFORMATION = 0x001C; 101 /** Date and time GPSDateStamp 29 1D ASCII 11 */ 101 102 public static final int TAG_DATE_STAMP = 0x001D; 103 /** Whether differential correction is applied GPSDifferential 30 1E SHORT 1 */ 102 104 public static final int TAG_DIFFERENTIAL = 0x001E; 105 /** Horizontal positioning errors GPSHPositioningError 31 1F RATIONAL 1 */ 106 public static final int TAG_H_POSITIONING_ERROR = 0x001F; 103 107 104 108 @NotNull … … 140 144 _tagNameMap.put(TAG_DATE_STAMP, "GPS Date Stamp"); 141 145 _tagNameMap.put(TAG_DIFFERENTIAL, "GPS Differential"); 146 _tagNameMap.put(TAG_H_POSITIONING_ERROR, "GPS H Positioning Error"); 142 147 } 143 148 -
trunk/src/com/drew/metadata/exif/PanasonicRawDistortionDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawDistortionDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawIFD0Descriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawIFD0Directory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawWbInfo2Descriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawWbInfo2Directory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawWbInfoDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PanasonicRawWbInfoDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PrintIMDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/PrintIMDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/AppleMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/AppleMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/LeicaType5MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/LeicaType5MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 26 26 import com.drew.metadata.TagDescriptor; 27 27 28 import java.nio.ByteBuffer; 28 29 import java.text.DecimalFormat; 29 30 … … 105 106 public String getPowerUpTimeDescription() 106 107 { 107 return getEpochTimeDescription(TAG_POWER_UP_TIME); 108 // this is generally a byte[] of length 8 directly representing a date and time. 109 // the format is : first 2 bytes together are the year, and then each byte after 110 // is month, day, hour, minute, second with the eighth byte unused 111 // e.g., 2011:04:25 01:54:58 112 113 byte[] values = _directory.getByteArray(TAG_POWER_UP_TIME); 114 short year = ByteBuffer.wrap(new byte[]{values[0], values[1]}).getShort(); 115 return String.format("%04d:%02d:%02d %02d:%02d:%02d", year, values[2], values[3], 116 values[4], values[5], values[6]); 108 117 } 109 118 … … 334 343 335 344 @Nullable 345 public String getLensFocusDistance() 346 { 347 int[] values = _directory.getDecryptedIntArray(TAG_LENS_DATA); 348 349 if (values == null || values.length < 11) 350 return null; 351 352 return String.format("%.2fm", getDistanceInMeters(values[10])); 353 } 354 355 @Nullable 336 356 public String getHueAdjustmentDescription() 337 357 { … … 351 371 return getVersionBytesDescription(TAG_FIRMWARE_VERSION, 2); 352 372 } 373 374 private double getDistanceInMeters(int val) 375 { 376 if (val < 0) 377 val += 256; 378 return 0.01 * Math.pow(10, val / 40.0f); 379 } 353 380 } -
trunk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 21 21 package com.drew.metadata.exif.makernotes; 22 22 23 import com.drew.lang.annotations.Nullable; 23 24 import com.drew.lang.annotations.NotNull; 24 25 import com.drew.metadata.Directory; … … 923 924 return _tagNameMap; 924 925 } 926 927 /** Nikon decryption tables used in exiftool */ 928 private static final int[] _decTable1 = {0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d, 929 0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d, 930 0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f, 931 0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f, 932 0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1, 933 0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17, 934 0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89, 935 0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f, 936 0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b, 937 0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb, 938 0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3, 939 0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f, 940 0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35, 941 0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43, 942 0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5, 943 0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 }; 944 private static final int[] _decTable2 = { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c, 945 0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34, 946 0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad, 947 0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05, 948 0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee, 949 0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d, 950 0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b, 951 0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b, 952 0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc, 953 0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33, 954 0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8, 955 0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6, 956 0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c, 957 0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49, 958 0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb, 959 0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f }; 960 961 962 /** decryption algorithm adapted from exiftool */ 963 @Nullable 964 public int[] getDecryptedIntArray(int tagType) 965 { 966 int[] data = getIntArray(tagType); 967 Integer serial = getInteger(TAG_CAMERA_SERIAL_NUMBER); 968 Integer count = getInteger(TAG_EXPOSURE_SEQUENCE_NUMBER); 969 970 if (data == null || serial == null || count == null) 971 return null; 972 973 int key = 0; 974 for (int i = 0; i < 4; i++) 975 key ^= (count >> (i * 8)) & 0xff; 976 977 int ci = _decTable1[serial & 0xff]; 978 int cj = _decTable2[key]; 979 int ck = 0x60; 980 981 for (int i = 4; i < data.length; i++) 982 { 983 cj = (cj + ci * ck) & 0xff; 984 ck = (ck + 1) & 0xff; 985 data[i] ^= cj; 986 } 987 988 return data; 989 } 925 990 } -
trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopment2MakernoteDescriptor.java
r13061 r15217 111 111 if (((v >> 1) & 1) != 0) sb.append("Noise Filter, "); 112 112 if (((v >> 2) & 1) != 0) sb.append("Noise Filter (ISO Boost), "); 113 114 return sb.substring(0, sb.length() - 2); 113 if (((v >> 3) & 1) != 0) sb.append("Noise Filter (Auto), "); 114 115 if (sb.length() > 2) { 116 sb.delete(sb.length() - 2, sb.length()); 117 } 118 return sb.toString(); 115 119 } 116 120 -
trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/ReconyxHyperFireMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/ReconyxHyperFireMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/ReconyxUltraFireMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/ReconyxUltraFireMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SamsungType2MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SamsungType2MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/iptc/IptcDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/iptc/IptcDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/iptc/IptcReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 118 118 } 119 119 120 // we need at least f ivebytes left to read a tag121 if (offset + 5> length) {120 // we need at least four bytes left to read a tag 121 if (offset + 4 > length) { 122 122 directory.addError("Too few bytes remain for a valid IPTC tag"); 123 123 return; … … 130 130 directoryType = reader.getUInt8(); 131 131 tagType = reader.getUInt8(); 132 // TODO support Extended DataSet Tag (see 1.5(c), p14, IPTC-IIMV4.2.pdf)133 132 tagByteCount = reader.getUInt16(); 133 if (tagByteCount > 32767) { 134 // Extended DataSet Tag (see 1.5(c), p14, IPTC-IIMV4.2.pdf) 135 tagByteCount = ((tagByteCount & 0x7FFF) << 16) | reader.getUInt16(); 136 offset += 2; 137 } 134 138 offset += 4; 135 139 } catch (IOException e) { -
trunk/src/com/drew/metadata/iptc/Iso2022Converter.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 28 28 29 29 /** 30 * Provides a human-readable string version of the tag stored in a HuffmanTableDirectory.30 * Provides a human-readable string version of the tag stored in a {@link HuffmanTablesDirectory}. 31 31 * 32 32 * <ul> … … 61 61 { 62 62 Integer value = _directory.getInteger(TAG_NUMBER_OF_TABLES); 63 if (value ==null)63 if (value == null) 64 64 return null; 65 65 return value + (value == 1 ? " Huffman table" : " Huffman tables"); -
trunk/src/com/drew/metadata/jpeg/HuffmanTablesDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 25 25 import java.util.HashMap; 26 26 import java.util.List; 27 27 28 import com.drew.lang.annotations.NotNull; 28 29 import com.drew.metadata.Directory; … … 168 169 /** 169 170 * @return The {@link List} of {@link HuffmanTable}s in this 170 * 171 * {@link Directory}. 171 172 */ 172 173 @NotNull … … 227 228 */ 228 229 public static class HuffmanTable { 229 private final int tableLength;230 private final HuffmanTableClass tableClass;231 private final int tableDestinationId;232 private final byte[] lengthBytes;233 private final byte[] valueBytes;234 235 public HuffmanTable (236 @NotNull HuffmanTableClass237 tableClass,230 private final int _tableLength; 231 private final HuffmanTableClass _tableClass; 232 private final int _tableDestinationId; 233 private final byte[] _lengthBytes; 234 private final byte[] _valueBytes; 235 236 @SuppressWarnings("ConstantConditions") 237 public HuffmanTable( 238 @NotNull HuffmanTableClass tableClass, 238 239 int tableDestinationId, 239 @NotNull byte[] lBytes, 240 @NotNull byte[] vBytes 241 ) { 242 this.tableClass = tableClass; 243 this.tableDestinationId = tableDestinationId; 244 this.lengthBytes = lBytes; 245 this.valueBytes = vBytes; 246 this.tableLength = vBytes.length + 17; 240 @NotNull byte[] lengthBytes, 241 @NotNull byte[] valueBytes) 242 { 243 if (lengthBytes == null) 244 throw new IllegalArgumentException("lengthBytes cannot be null."); 245 if (valueBytes == null) 246 throw new IllegalArgumentException("valueBytes cannot be null."); 247 248 _tableClass = tableClass; 249 _tableDestinationId = tableDestinationId; 250 _lengthBytes = lengthBytes; 251 _valueBytes = valueBytes; 252 _tableLength = _valueBytes.length + 17; 247 253 } 248 254 … … 251 257 */ 252 258 public int getTableLength() { 253 return tableLength; 254 } 255 259 return _tableLength; 260 } 256 261 257 262 /** … … 259 264 */ 260 265 public HuffmanTableClass getTableClass() { 261 return tableClass; 262 } 263 266 return _tableClass; 267 } 264 268 265 269 /** … … 267 271 */ 268 272 public int getTableDestinationId() { 269 return tableDestinationId; 270 } 271 273 return _tableDestinationId; 274 } 272 275 273 276 /** 274 277 * @return A byte array with the L values for this table. 275 278 */ 279 @NotNull 276 280 public byte[] getLengthBytes() { 277 if (lengthBytes == null) 278 return null; 279 byte[] result = new byte[lengthBytes.length]; 280 System.arraycopy(lengthBytes, 0, result, 0, lengthBytes.length); 281 byte[] result = new byte[_lengthBytes.length]; 282 System.arraycopy(_lengthBytes, 0, result, 0, _lengthBytes.length); 281 283 return result; 282 284 } 283 285 284 285 286 /** 286 287 * @return A byte array with the V values for this table. 287 288 */ 289 @NotNull 288 290 public byte[] getValueBytes() { 289 if (valueBytes == null) 290 return null; 291 byte[] result = new byte[valueBytes.length]; 292 System.arraycopy(valueBytes, 0, result, 0, valueBytes.length); 291 byte[] result = new byte[_valueBytes.length]; 292 System.arraycopy(_valueBytes, 0, result, 0, _valueBytes.length); 293 293 return result; 294 294 } … … 318 318 */ 319 319 public boolean isTypical() { 320 if ( tableClass == HuffmanTableClass.DC) {320 if (_tableClass == HuffmanTableClass.DC) { 321 321 return 322 Arrays.equals( lengthBytes, TYPICAL_LUMINANCE_DC_LENGTHS) &&323 Arrays.equals( valueBytes, TYPICAL_LUMINANCE_DC_VALUES) ||324 Arrays.equals( lengthBytes, TYPICAL_CHROMINANCE_DC_LENGTHS) &&325 Arrays.equals( valueBytes, TYPICAL_CHROMINANCE_DC_VALUES);326 } else if ( tableClass == HuffmanTableClass.AC) {322 Arrays.equals(_lengthBytes, TYPICAL_LUMINANCE_DC_LENGTHS) && 323 Arrays.equals(_valueBytes, TYPICAL_LUMINANCE_DC_VALUES) || 324 Arrays.equals(_lengthBytes, TYPICAL_CHROMINANCE_DC_LENGTHS) && 325 Arrays.equals(_valueBytes, TYPICAL_CHROMINANCE_DC_VALUES); 326 } else if (_tableClass == HuffmanTableClass.AC) { 327 327 return 328 Arrays.equals( lengthBytes, TYPICAL_LUMINANCE_AC_LENGTHS) &&329 Arrays.equals( valueBytes, TYPICAL_LUMINANCE_AC_VALUES) ||330 Arrays.equals( lengthBytes, TYPICAL_CHROMINANCE_AC_LENGTHS) &&331 Arrays.equals( valueBytes, TYPICAL_CHROMINANCE_AC_VALUES);328 Arrays.equals(_lengthBytes, TYPICAL_LUMINANCE_AC_LENGTHS) && 329 Arrays.equals(_valueBytes, TYPICAL_LUMINANCE_AC_VALUES) || 330 Arrays.equals(_lengthBytes, TYPICAL_CHROMINANCE_AC_LENGTHS) && 331 Arrays.equals(_valueBytes, TYPICAL_CHROMINANCE_AC_VALUES); 332 332 } 333 333 return false; … … 351 351 public static HuffmanTableClass typeOf(int value) { 352 352 switch (value) { 353 case 0: return DC; 354 case 1 : return AC; 355 default: return UNKNOWN; 353 case 0: 354 return DC; 355 case 1: 356 return AC; 357 default: 358 return UNKNOWN; 356 359 } 357 360 } -
trunk/src/com/drew/metadata/jpeg/JpegCommentDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegCommentDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegCommentReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegComponent.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegDescriptor.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegDhtReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegDirectory.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegDnlReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 30 30 31 31 import java.io.IOException; 32 import java.util.Arrays;33 32 import java.util.Collections; 34 33 -
trunk/src/com/drew/metadata/jpeg/JpegReader.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/tiff/DirectoryTiffHandler.java
r13061 r15217 1 1 /* 2 * Copyright 2002-201 7 Drew Noakes2 * Copyright 2002-2019 Drew Noakes and contributors 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 24 24 import com.drew.lang.Rational; 25 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 26 27 import com.drew.metadata.Directory; 27 28 import com.drew.metadata.ErrorDirectory; … … 40 41 private final Stack<Directory> _directoryStack = new Stack<Directory>(); 41 42 42 protected Directory _currentDirectory; 43 @Nullable private Directory _rootParentDirectory; 44 @Nullable protected Directory _currentDirectory; 43 45 protected final Metadata _metadata; 44 46 45 protected DirectoryTiffHandler(Metadata metadata )47 protected DirectoryTiffHandler(Metadata metadata, @Nullable Directory parentDirectory) 46 48 { 47 49 _metadata = metadata; 50 _rootParentDirectory = parentDirectory; 48 51 } 49 52 … … 55 58 protected void pushDirectory(@NotNull Class<? extends Directory> directoryClass) 56 59 { 57 Directory newDirectory = null;60 Directory newDirectory; 58 61 59 62 try { … … 65 68 } 66 69 67 if (newDirectory != null) 68 { 69 // If this is the first directory, don't add to the stack 70 if (_currentDirectory != null) 71 { 72 _directoryStack.push(_currentDirectory); 73 newDirectory.setParent(_currentDirectory); 70 // If this is the first directory, don't add to the stack 71 if (_currentDirectory == null) { 72 // Apply any pending root parent to this new directory 73 if (_rootParentDirectory != null) { 74 newDirectory.setParent(_rootParentDirectory); 75 _rootParentDirectory = null; 74 76 } 75 _currentDirectory = newDirectory;76 _metadata.addDirectory(_currentDirectory);77 77 } 78 else { 79 // The current directory is pushed onto the stack, and set as the new directory's parent 80 _directoryStack.push(_currentDirectory); 81 newDirectory.setParent(_currentDirectory); 82 } 83 84 _currentDirectory = newDirectory; 85 _metadata.addDirectory(_currentDirectory); 78 86 } 79 87
Note:
See TracChangeset
for help on using the changeset viewer.