Changeset 10862 in josm for trunk/src/com/drew
- Timestamp:
- 2016-08-20T20:58:03+02:00 (8 years ago)
- Location:
- trunk/src/com/drew
- Files:
-
- 5 added
- 2 deleted
- 106 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/imaging/ImageProcessingException.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/PhotographicConversions.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 39 39 import com.drew.metadata.iptc.IptcReader; 40 40 //import com.drew.metadata.jfif.JfifReader; 41 //import com.drew.metadata.jfxx.JfxxReader; 41 42 import com.drew.metadata.jpeg.JpegCommentReader; 42 43 import com.drew.metadata.jpeg.JpegReader; 44 //import com.drew.metadata.photoshop.DuckyReader; 43 45 //import com.drew.metadata.photoshop.PhotoshopReader; 44 46 //import com.drew.metadata.xmp.XmpReader; … … 55 57 new JpegCommentReader(), 56 58 //new JfifReader(), 59 //new JfxxReader(), 57 60 new ExifReader(), 58 61 //new XmpReader(), 59 62 //new IccReader(), 60 63 //new PhotoshopReader(), 64 //new DuckyReader(), 61 65 new IptcReader()//, 62 66 //new AdobeJpegReader() -
trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 52 52 * @param segmentBytes the byte array holding data for the segment being added 53 53 */ 54 @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})55 54 public void addSegment(byte segmentType, @NotNull byte[] segmentBytes) 56 55 { … … 207 206 * @param occurrence the zero-based index of the segment occurrence to remove. 208 207 */ 209 @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})210 208 public void removeSegmentOccurrence(@NotNull JpegSegmentType segmentType, int occurrence) 211 209 { … … 220 218 * @param occurrence the zero-based index of the segment occurrence to remove. 221 219 */ 222 @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})223 220 public void removeSegmentOccurrence(byte segmentType, int occurrence) 224 221 { -
trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 43 43 public class JpegSegmentReader 44 44 { 45 /** 46 * The 0xFF byte that signals the start of a segment. 47 */ 48 private static final byte SEGMENT_IDENTIFIER = (byte) 0xFF; 49 45 50 /** 46 51 * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet). … … 112 117 // by a 0xFF and then a byte not equal to 0x00 or 0xFF. 113 118 114 final short segmentIdentifier = reader.getUInt8(); 119 byte segmentIdentifier = reader.getInt8(); 120 byte segmentType = reader.getInt8(); 115 121 116 // We must have at least one 0xFF byte 117 if (segmentIdentifier != 0xFF) 118 throw new JpegProcessingException("Expected JPEG segment start identifier 0xFF, not 0x" + Integer.toHexString(segmentIdentifier).toUpperCase()); 119 120 // Read until we have a non-0xFF byte. This identifies the segment type. 121 byte segmentType = reader.getInt8(); 122 while (segmentType == (byte)0xFF) 123 segmentType = reader.getInt8(); 124 125 if (segmentType == 0) 126 throw new JpegProcessingException("Expected non-zero byte as part of JPEG marker identifier"); 122 // Read until we have a 0xFF byte followed by a byte that is not 0xFF or 0x00 123 while (segmentIdentifier != SEGMENT_IDENTIFIER || segmentType == SEGMENT_IDENTIFIER || segmentType == 0) { 124 segmentIdentifier = segmentType; 125 segmentType = reader.getInt8(); 126 } 127 127 128 128 if (segmentType == SEGMENT_SOS) { -
trunk/src/com/drew/imaging/jpeg/JpegSegmentType.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 30 30 * An enumeration of the known segment types found in JPEG files. 31 31 * 32 * <ul> 33 * <li>http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html</li> 34 * <li>http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html</li> 35 * </ul> 36 * 32 37 * @author Drew Noakes https://drewnoakes.com 33 38 */ 34 39 public enum JpegSegmentType 35 40 { 36 /** APP0 JPEG segment identifier -- JFIF data (also JFXX apparently). */41 /** APP0 JPEG segment identifier. Commonly contains JFIF, JFXX. */ 37 42 APP0((byte)0xE0, true), 38 43 39 /** APP1 JPEG segment identifier -- where Exif data is kept.XMP data is also kept in here, though usually in a second instance. */44 /** APP1 JPEG segment identifier. Commonly contains Exif. XMP data is also kept in here, though usually in a second instance. */ 40 45 APP1((byte)0xE1, true), 41 46 42 /** APP2 JPEG segment identifier. */47 /** APP2 JPEG segment identifier. Commonly contains ICC. */ 43 48 APP2((byte)0xE2, true), 44 49 … … 64 69 APP9((byte)0xE9, true), 65 70 66 /** APPA (App10) JPEG segment identifier -- can hold Unicodecomments. */71 /** APPA (App10) JPEG segment identifier. Can contain Unicode comments, though {@link JpegSegmentType#COM} is more commonly used for comments. */ 67 72 APPA((byte)0xEA, true), 68 73 … … 73 78 APPC((byte)0xEC, true), 74 79 75 /** APPD (App13) JPEG segment identifier -- IPTC data in here. */80 /** APPD (App13) JPEG segment identifier. Commonly contains IPTC, Photoshop data. */ 76 81 APPD((byte)0xED, true), 77 82 78 /** APPE (App14) JPEG segment identifier. */83 /** APPE (App14) JPEG segment identifier. Commonly contains Adobe data. */ 79 84 APPE((byte)0xEE, true), 80 85 -
trunk/src/com/drew/imaging/jpeg/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffDataFormat.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffHandler.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 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 27 28 import java.io.IOException; … … 46 47 void setTiffMarker(int marker) throws TiffProcessingException; 47 48 48 boolean isTagIfdPointer(int tagType);49 boolean tryEnterSubIfd(int tagId); 49 50 boolean hasFollowerIfd(); 50 51 … … 52 53 53 54 void completed(@NotNull final RandomAccessReader reader, final int tiffHeaderOffset); 55 56 @Nullable 57 Long tryCustomProcessFormat(int tagId, int formatCode, long componentCount); 54 58 55 59 boolean customProcessTag(int tagOffset, -
trunk/src/com/drew/imaging/tiff/TiffProcessingException.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/imaging/tiff/TiffReader.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 110 110 final int tiffHeaderOffset) throws IOException 111 111 { 112 Boolean resetByteOrder = null; 112 113 try { 113 114 // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist … … 126 127 // First two bytes in the IFD are the number of tags in this directory 127 128 int dirTagCount = reader.getUInt16(ifdOffset); 129 130 // Some software modifies the byte order of the file, but misses some IFDs (such as makernotes). 131 // The entire test image repository doesn't contain a single IFD with more than 255 entries. 132 // Here we detect switched bytes that suggest this problem, and temporarily swap the byte order. 133 // This was discussed in GitHub issue #136. 134 if (dirTagCount > 0xFF && (dirTagCount & 0xFF) == 0) { 135 resetByteOrder = reader.isMotorolaByteOrder(); 136 dirTagCount >>= 8; 137 reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder()); 138 } 128 139 129 140 int dirLength = (2 + (12 * dirTagCount) + 4); … … 147 158 final TiffDataFormat format = TiffDataFormat.fromTiffFormatCode(formatCode); 148 159 160 // 4 bytes dictate the number of components in this tag's data 161 final long componentCount = reader.getUInt32(tagOffset + 4); 162 163 final long byteCount; 149 164 if (format == null) { 150 // This error suggests that we are processing at an incorrect index and will generate 151 // rubbish until we go out of bounds (which may be a while). Exit now. 152 handler.error("Invalid TIFF tag format code: " + formatCode); 153 // TODO specify threshold as a parameter, or provide some other external control over this behaviour 154 if (++invalidTiffFormatCodeCount > 5) { 155 handler.error("Stopping processing as too many errors seen in TIFF IFD"); 156 return; 165 Long byteCountOverride = handler.tryCustomProcessFormat(tagId, formatCode, componentCount); 166 if (byteCountOverride == null) { 167 // This error suggests that we are processing at an incorrect index and will generate 168 // rubbish until we go out of bounds (which may be a while). Exit now. 169 handler.error(String.format("Invalid TIFF tag format code %d for tag 0x%04X", formatCode, tagId)); 170 // TODO specify threshold as a parameter, or provide some other external control over this behaviour 171 if (++invalidTiffFormatCodeCount > 5) { 172 handler.error("Stopping processing as too many errors seen in TIFF IFD"); 173 return; 174 } 175 continue; 157 176 } 158 continue; 159 } 160 161 // 4 bytes dictate the number of components in this tag's data 162 final int componentCount = reader.getInt32(tagOffset + 4); 163 if (componentCount < 0) { 164 handler.error("Negative TIFF tag component count"); 165 continue; 166 } 167 168 final int byteCount = componentCount * format.getComponentSizeBytes(); 169 170 final int tagValueOffset; 177 byteCount = byteCountOverride; 178 } else { 179 byteCount = componentCount * format.getComponentSizeBytes(); 180 } 181 182 final long tagValueOffset; 171 183 if (byteCount > 4) { 172 184 // If it's bigger than 4 bytes, the dir entry contains an offset. 173 final int offsetVal = reader.getInt32(tagOffset + 8);185 final long offsetVal = reader.getUInt32(tagOffset + 8); 174 186 if (offsetVal + byteCount > reader.getLength()) { 175 187 // Bogus pointer offset and / or byteCount value … … 195 207 } 196 208 197 // 198 // Special handling for tags that point to other IFDs199 //200 if (byteCount == 4 && handler.isTagIfdPointer(tagId)) {201 final int subDirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset);202 processIfd(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset);203 } else {204 if (!handler.customProcessTag(tagValueOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, byteCount)) {205 processTag(handler, tagId, tagValueOffset, componentCount, formatCode, reader);209 // Some tags point to one or more additional IFDs to process 210 boolean isIfdPointer = false; 211 if (byteCount == 4 * componentCount) { 212 for (int i = 0; i < componentCount; i++) { 213 if (handler.tryEnterSubIfd(tagId)) { 214 isIfdPointer = true; 215 int subDirOffset = tiffHeaderOffset + reader.getInt32((int) (tagValueOffset + i * 4)); 216 processIfd(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset); 217 } 206 218 } 219 } 220 221 // If it wasn't an IFD pointer, allow custom tag processing to occur 222 if (!isIfdPointer && !handler.customProcessTag((int) tagValueOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, (int) byteCount)) { 223 // If no custom processing occurred, process the tag in the standard fashion 224 processTag(handler, tagId, (int) tagValueOffset, (int) componentCount, formatCode, reader); 207 225 } 208 226 } … … 229 247 } finally { 230 248 handler.endingIFD(); 249 if (resetByteOrder != null) 250 reader.setMotorolaByteOrder(resetByteOrder); 231 251 } 232 252 } … … 350 370 break; 351 371 default: 352 handler.error(String.format(" Unknown format code %d for tag %d", formatCode, tagId));372 handler.error(String.format("Invalid TIFF tag format code %d for tag 0x%04X", formatCode, tagId)); 353 373 } 354 374 } -
trunk/src/com/drew/imaging/tiff/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/BufferBoundsException.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/ByteArrayReader.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 22 22 package com.drew.lang; 23 23 24 import java.io.IOException; 25 24 26 import com.drew.lang.annotations.NotNull; 25 26 import java.io.IOException;27 27 28 28 /** … … 40 40 private final byte[] _buffer; 41 41 42 @SuppressWarnings({ "ConstantConditions" })43 @com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")44 42 public ByteArrayReader(@NotNull byte[] buffer) 45 43 { … … 74 72 return bytesRequested >= 0 75 73 && index >= 0 76 && (long)index + (long)bytesRequested - 1L < (long)_buffer.length;74 && (long)index + (long)bytesRequested - 1L < _buffer.length; 77 75 } 78 76 -
trunk/src/com/drew/lang/CompoundException.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/GeoLocation.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 84 84 double[] dms = decimalToDegreesMinutesSeconds(decimal); 85 85 DecimalFormat format = new DecimalFormat("0.##"); 86 return String.format("%s °%s' %s\"", format.format(dms[0]), format.format(dms[1]), format.format(dms[2]));86 return String.format("%s\u00B0 %s' %s\"", format.format(dms[0]), format.format(dms[1]), format.format(dms[2])); 87 87 } 88 88 -
trunk/src/com/drew/lang/RandomAccessReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 53 53 * @param index The index from which to read the byte 54 54 * @return The read byte value 55 * @throws IllegalArgumentException <code>index</code> or <code>count</code> arenegative55 * @throws IllegalArgumentException <code>index</code> is negative 56 56 * @throws BufferBoundsException if the requested byte is beyond the end of the underlying data source 57 57 * @throws IOException if the byte is unable to be read -
trunk/src/com/drew/lang/Rational.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/SequentialByteArrayReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 42 42 } 43 43 44 @SuppressWarnings("ConstantConditions")45 44 public SequentialByteArrayReader(@NotNull byte[] bytes, int baseIndex) 46 45 { -
trunk/src/com/drew/lang/SequentialReader.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/StreamReader.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 37 37 private final InputStream _stream; 38 38 39 @SuppressWarnings("ConstantConditions")40 39 public StreamReader(@NotNull InputStream stream) 41 40 { -
trunk/src/com/drew/lang/StringUtil.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/annotations/NotNull.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/annotations/Nullable.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/annotations/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/lang/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Age.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Directory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 24 24 import com.drew.lang.annotations.NotNull; 25 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.lang.annotations.SuppressWarnings;27 26 28 27 import java.io.UnsupportedEncodingException; 29 28 import java.lang.reflect.Array; 30 29 import java.text.DateFormat; 30 import java.text.DecimalFormat; 31 31 import java.text.ParseException; 32 32 import java.text.SimpleDateFormat; 33 33 import java.util.*; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 34 36 35 37 /** … … 41 43 public abstract class Directory 42 44 { 45 private static final DecimalFormat _floatFormat = new DecimalFormat("0.###"); 46 43 47 /** Map of values hashed by type identifiers. */ 44 48 @NotNull … … 59 63 protected TagDescriptor _descriptor; 60 64 65 @Nullable 66 private Directory _parent; 67 61 68 // ABSTRACT METHODS 62 69 … … 171 178 { 172 179 return _errorList.size(); 180 } 181 182 @Nullable 183 public Directory getParent() 184 { 185 return _parent; 186 } 187 188 public void setParent(@NotNull Directory parent) 189 { 190 _parent = parent; 173 191 } 174 192 … … 701 719 /** Returns the specified tag's value as a boolean. If the tag is not set or cannot be converted, <code>null</code> is returned. */ 702 720 @Nullable 703 @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")704 721 public Boolean getBooleanObject(int tagType) 705 722 { … … 725 742 * <p> 726 743 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 727 * the current{@link TimeZone}. If the {@link TimeZone} is known, call the overload that accepts one as an argument.744 * the GMT {@link TimeZone}. If the {@link TimeZone} is known, call the overload that accepts one as an argument. 728 745 */ 729 746 @Nullable 730 747 public java.util.Date getDate(int tagType) 731 748 { 732 return getDate(tagType, null );749 return getDate(tagType, null, null); 733 750 } 734 751 … … 738 755 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 739 756 * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). Note that this parameter 740 * is only considered if the underlying value is a string and parsing occurs, otherwise it has no effect.757 * is only considered if the underlying value is a string and it has no time zone information, otherwise it has no effect. 741 758 */ 742 759 @Nullable 743 760 public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone) 744 761 { 745 Object o = getObject(tagType); 746 747 if (o == null) 748 return null; 762 return getDate(tagType, null, timeZone); 763 } 764 765 /** 766 * Returns the specified tag's value as a java.util.Date. If the value is unset or cannot be converted, <code>null</code> is returned. 767 * <p> 768 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 769 * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). Note that this parameter 770 * is only considered if the underlying value is a string and it has no time zone information, otherwise it has no effect. 771 * In addition, the {@code subsecond} parameter, which specifies the number of digits after the decimal point in the seconds, 772 * is set to the returned Date. This parameter is only considered if the underlying value is a string and is has 773 * no subsecond information, otherwise it has no effect. 774 * 775 * @param tagType the tag identifier 776 * @param subsecond the subsecond value for the Date 777 * @param timeZone the time zone to use 778 * @return a Date representing the time value 779 */ 780 @Nullable 781 public java.util.Date getDate(int tagType, @Nullable String subsecond, @Nullable TimeZone timeZone) 782 { 783 Object o = getObject(tagType); 749 784 750 785 if (o instanceof java.util.Date) 751 786 return (java.util.Date)o; 752 787 788 java.util.Date date = null; 789 753 790 if (o instanceof String) { 754 // This seems to cover all known Exif date strings791 // This seems to cover all known Exif and Xmp date strings 755 792 // Note that " : : : : " is a valid date string according to the Exif spec (which means 'unknown date'): http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html 756 793 String datePatterns[] = { … … 760 797 "yyyy-MM-dd HH:mm", 761 798 "yyyy.MM.dd HH:mm:ss", 762 "yyyy.MM.dd HH:mm" }; 799 "yyyy.MM.dd HH:mm", 800 "yyyy-MM-dd'T'HH:mm:ss", 801 "yyyy-MM-dd'T'HH:mm", 802 "yyyy-MM-dd", 803 "yyyy-MM", 804 "yyyy" }; 763 805 String dateString = (String)o; 806 807 // if the date string has subsecond information, it supersedes the subsecond parameter 808 Pattern subsecondPattern = Pattern.compile("(\\d\\d:\\d\\d:\\d\\d)(\\.\\d+)"); 809 Matcher subsecondMatcher = subsecondPattern.matcher(dateString); 810 if (subsecondMatcher.find()) { 811 subsecond = subsecondMatcher.group(2).substring(1); 812 dateString = subsecondMatcher.replaceAll("$1"); 813 } 814 815 // if the date string has time zone information, it supersedes the timeZone parameter 816 Pattern timeZonePattern = Pattern.compile("(Z|[+-]\\d\\d:\\d\\d)$"); 817 Matcher timeZoneMatcher = timeZonePattern.matcher(dateString); 818 if (timeZoneMatcher.find()) { 819 timeZone = TimeZone.getTimeZone("GMT" + timeZoneMatcher.group().replaceAll("Z", "")); 820 dateString = timeZoneMatcher.replaceAll(""); 821 } 822 764 823 for (String datePattern : datePatterns) { 765 824 try { … … 770 829 parser.setTimeZone(TimeZone.getTimeZone("GMT")); // don't interpret zone time 771 830 772 return parser.parse(dateString); 831 date = parser.parse(dateString); 832 break; 773 833 } catch (ParseException ex) { 774 834 // simply try the next pattern … … 776 836 } 777 837 } 778 return null; 838 839 if (date == null) 840 return null; 841 842 if (subsecond == null) 843 return date; 844 845 try { 846 int millisecond = (int) (Double.parseDouble("." + subsecond) * 1000); 847 if (millisecond >= 0 && millisecond < 1000) { 848 Calendar calendar = Calendar.getInstance(); 849 calendar.setTime(date); 850 calendar.set(Calendar.MILLISECOND, millisecond); 851 return calendar.getTime(); 852 } 853 return date; 854 } catch (NumberFormatException e) { 855 return date; 856 } 779 857 } 780 858 … … 835 913 int arrayLength = Array.getLength(o); 836 914 final Class<?> componentType = o.getClass().getComponentType(); 837 boolean isObjectArray = Object.class.isAssignableFrom(componentType); 838 boolean isFloatArray = componentType.getName().equals("float"); 839 boolean isDoubleArray = componentType.getName().equals("double"); 840 boolean isIntArray = componentType.getName().equals("int"); 841 boolean isLongArray = componentType.getName().equals("long"); 842 boolean isByteArray = componentType.getName().equals("byte"); 843 boolean isShortArray = componentType.getName().equals("short"); 915 844 916 StringBuilder string = new StringBuilder(); 845 for (int i = 0; i < arrayLength; i++) { 846 if (i != 0) 847 string.append(' '); 848 if (isObjectArray) 917 918 if (Object.class.isAssignableFrom(componentType)) { 919 // object array 920 for (int i = 0; i < arrayLength; i++) { 921 if (i != 0) 922 string.append(' '); 849 923 string.append(Array.get(o, i).toString()); 850 else if (isIntArray) 924 } 925 } else if (componentType.getName().equals("int")) { 926 for (int i = 0; i < arrayLength; i++) { 927 if (i != 0) 928 string.append(' '); 851 929 string.append(Array.getInt(o, i)); 852 else if (isShortArray) 930 } 931 } else if (componentType.getName().equals("short")) { 932 for (int i = 0; i < arrayLength; i++) { 933 if (i != 0) 934 string.append(' '); 853 935 string.append(Array.getShort(o, i)); 854 else if (isLongArray) 936 } 937 } else if (componentType.getName().equals("long")) { 938 for (int i = 0; i < arrayLength; i++) { 939 if (i != 0) 940 string.append(' '); 855 941 string.append(Array.getLong(o, i)); 856 else if (isFloatArray) 857 string.append(Array.getFloat(o, i)); 858 else if (isDoubleArray) 859 string.append(Array.getDouble(o, i)); 860 else if (isByteArray) 861 string.append(Array.getByte(o, i)); 862 else 863 addError("Unexpected array component type: " + componentType.getName()); 864 } 942 } 943 } else if (componentType.getName().equals("float")) { 944 for (int i = 0; i < arrayLength; i++) { 945 if (i != 0) 946 string.append(' '); 947 string.append(_floatFormat.format(Array.getFloat(o, i))); 948 } 949 } else if (componentType.getName().equals("double")) { 950 for (int i = 0; i < arrayLength; i++) { 951 if (i != 0) 952 string.append(' '); 953 string.append(_floatFormat.format(Array.getDouble(o, i))); 954 } 955 } else if (componentType.getName().equals("byte")) { 956 for (int i = 0; i < arrayLength; i++) { 957 if (i != 0) 958 string.append(' '); 959 string.append(Array.getByte(o, i) & 0xff); 960 } 961 } else { 962 addError("Unexpected array component type: " + componentType.getName()); 963 } 964 865 965 return string.toString(); 866 966 } 967 968 if (o instanceof Double) 969 return _floatFormat.format(((Double)o).doubleValue()); 970 971 if (o instanceof Float) 972 return _floatFormat.format(((Float)o).floatValue()); 867 973 868 974 // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show -
trunk/src/com/drew/metadata/Face.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Metadata.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 36 36 public final class Metadata 37 37 { 38 /** 39 * The list of {@link Directory} instances in this container, in the order they were added. 40 */ 38 41 @NotNull 39 private final Map<Class<? extends Directory>,Collection<Directory>> _directoryListByClass = new HashMap<Class<? extends Directory>, Collection<Directory>>();42 private final List<Directory> _directories = new ArrayList<>(); 40 43 41 44 /** … … 47 50 public Iterable<Directory> getDirectories() 48 51 { 49 return new DirectoryIterable(_directoryListByClass);52 return _directories; 50 53 } 51 54 52 55 @Nullable 56 @SuppressWarnings("unchecked") 53 57 public <T extends Directory> Collection<T> getDirectoriesOfType(Class<T> type) 54 58 { 55 return (Collection<T>)_directoryListByClass.get(type); 59 List<T> directories = new ArrayList<>(); 60 for (Directory dir : _directories) { 61 if (type.isAssignableFrom(dir.getClass())) { 62 directories.add((T)dir); 63 } 64 } 65 return directories; 56 66 } 57 67 … … 63 73 public int getDirectoryCount() 64 74 { 65 int count = 0; 66 for (Map.Entry<Class<? extends Directory>,Collection<Directory>> pair : _directoryListByClass.entrySet()) 67 count += pair.getValue().size(); 68 return count; 75 return _directories.size(); 69 76 } 70 77 … … 76 83 public <T extends Directory> void addDirectory(@NotNull T directory) 77 84 { 78 getOrCreateDirectoryList(directory.getClass()).add(directory);85 _directories.add(directory); 79 86 } 80 87 … … 91 98 public <T extends Directory> T getFirstDirectoryOfType(@NotNull Class<T> type) 92 99 { 93 // We suppress the warning here as the code asserts a map signature of Class<T>,T. 94 // So after get(Class<T>) it is for sure the result is from type T. 95 96 Collection<Directory> list = getDirectoryList(type); 97 98 if (list == null || list.isEmpty()) 99 return null; 100 101 return (T)list.iterator().next(); 100 for (Directory dir : _directories) { 101 if (type.isAssignableFrom(dir.getClass())) 102 return (T)dir; 103 } 104 return null; 102 105 } 103 106 … … 110 113 public boolean containsDirectoryOfType(Class<? extends Directory> type) 111 114 { 112 Collection<Directory> list = getDirectoryList(type); 113 return list != null && !list.isEmpty(); 115 for (Directory dir : _directories) { 116 if (type.isAssignableFrom(dir.getClass())) 117 return true; 118 } 119 return false; 114 120 } 115 121 … … 139 145 : "directories"); 140 146 } 141 142 @Nullable143 private <T extends Directory> Collection<Directory> getDirectoryList(@NotNull Class<T> type)144 {145 return _directoryListByClass.get(type);146 }147 148 @NotNull149 private <T extends Directory> Collection<Directory> getOrCreateDirectoryList(@NotNull Class<T> type)150 {151 Collection<Directory> collection = getDirectoryList(type);152 if (collection != null)153 return collection;154 collection = new ArrayList<Directory>();155 _directoryListByClass.put(type, collection);156 return collection;157 }158 159 private static class DirectoryIterable implements Iterable<Directory>160 {161 private final Map<Class<? extends Directory>, Collection<Directory>> _map;162 163 public DirectoryIterable(Map<Class<? extends Directory>, Collection<Directory>> map)164 {165 _map = map;166 }167 168 public Iterator<Directory> iterator()169 {170 return new DirectoryIterator(_map);171 }172 173 private static class DirectoryIterator implements Iterator<Directory>174 {175 @NotNull176 private final Iterator<Map.Entry<Class<? extends Directory>, Collection<Directory>>> _mapIterator;177 @Nullable178 private Iterator<Directory> _listIterator;179 180 public DirectoryIterator(Map<Class<? extends Directory>, Collection<Directory>> map)181 {182 _mapIterator = map.entrySet().iterator();183 184 if (_mapIterator.hasNext())185 _listIterator = _mapIterator.next().getValue().iterator();186 }187 188 public boolean hasNext()189 {190 return _listIterator != null && (_listIterator.hasNext() || _mapIterator.hasNext());191 }192 193 public Directory next()194 {195 if (_listIterator == null || (!_listIterator.hasNext() && !_mapIterator.hasNext()))196 throw new NoSuchElementException();197 198 while (!_listIterator.hasNext())199 _listIterator = _mapIterator.next().getValue().iterator();200 201 return _listIterator.next();202 }203 204 public void remove()205 {206 throw new UnsupportedOperationException();207 }208 }209 }210 147 } -
trunk/src/com/drew/metadata/MetadataException.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/MetadataReader.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/Tag.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 54 54 /** 55 55 * Gets the tag type in hex notation as a String with padded leading 56 * zeroes if necessary (i.e. <code>0x100 E</code>).56 * zeroes if necessary (i.e. <code>0x100e</code>). 57 57 * 58 58 * @return the tag type as a string in hexadecimal notation … … 61 61 public String getTagTypeHex() 62 62 { 63 String hex = Integer.toHexString(_tagType); 64 while (hex.length() < 4) hex = "0" + hex; 65 return "0x" + hex; 63 return String.format("0x%04x", _tagType); 66 64 } 67 65 … … 117 115 118 116 /** 119 * A basic representation of the tag's type and value. EG: <code>[ FNumber] F2.8</code>.117 * A basic representation of the tag's type and value. EG: <code>[Exif IFD0] FNumber - f/2.8</code>. 120 118 * 121 119 * @return the tag's type and value -
trunk/src/com/drew/metadata/TagDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 28 28 import java.io.UnsupportedEncodingException; 29 29 import java.lang.reflect.Array; 30 import java.math.RoundingMode; 31 import java.text.DecimalFormat; 32 import java.text.SimpleDateFormat; 30 33 import java.util.ArrayList; 31 34 import java.util.Date; … … 71 74 final int length = Array.getLength(object); 72 75 if (length > 16) { 73 final String componentTypeName = object.getClass().getComponentType().getName(); 74 return String.format("[%d %s%s]", length, componentTypeName, length == 1 ? "" : "s"); 76 return String.format("[%d %s]", length, length == 1 ? "value" : "values"); 75 77 } 78 } 79 80 if (object instanceof Date) 81 { 82 // Produce a date string having a format that includes the offset in form "+00:00" 83 return new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy") 84 .format((Date) object) 85 .replaceAll("([0-9]{2} [^ ]+)$", ":$1"); 76 86 } 77 87 … … 272 282 } 273 283 } 284 285 @Nullable 286 protected String getRationalOrDoubleString(int tagType) 287 { 288 Rational rational = _directory.getRational(tagType); 289 if (rational != null) 290 return rational.toSimpleString(true); 291 292 Double d = _directory.getDoubleObject(tagType); 293 if (d != null) 294 { 295 DecimalFormat format = new DecimalFormat("0.###"); 296 return format.format(d); 297 } 298 299 return null; 300 } 301 302 @Nullable 303 protected static String getFStopDescription(double fStop) 304 { 305 DecimalFormat format = new DecimalFormat("0.0"); 306 format.setRoundingMode(RoundingMode.HALF_UP); 307 return "f/" + format.format(fStop); 308 } 309 310 @Nullable 311 protected static String getFocalLengthDescription(double mm) 312 { 313 DecimalFormat format = new DecimalFormat("0.#"); 314 format.setRoundingMode(RoundingMode.HALF_UP); 315 return format.format(mm) + " mm"; 316 } 317 318 @Nullable 319 protected String getLensSpecificationDescription(int tag) 320 { 321 Rational[] values = _directory.getRationalArray(tag); 322 323 if (values == null || values.length != 4 || (values[0].doubleValue() == 0 && values[2].doubleValue() == 0)) 324 return null; 325 326 StringBuilder sb = new StringBuilder(); 327 328 if (values[0].equals(values[1])) 329 sb.append(values[0].toSimpleString(true)).append("mm"); 330 else 331 sb.append(values[0].toSimpleString(true)).append('-').append(values[1].toSimpleString(true)).append("mm"); 332 333 if (values[2].doubleValue() != 0) { 334 sb.append(' '); 335 336 DecimalFormat format = new DecimalFormat("0.0"); 337 format.setRoundingMode(RoundingMode.HALF_UP); 338 339 if (values[2].equals(values[3])) 340 sb.append(getFStopDescription(values[2].doubleValue())); 341 else 342 sb.append("f/").append(format.format(values[2].doubleValue())).append('-').append(format.format(values[3].doubleValue())); 343 } 344 345 return sb.toString(); 346 } 274 347 } -
trunk/src/com/drew/metadata/exif/ExifDescriptorBase.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 30 30 31 31 import java.io.UnsupportedEncodingException; 32 import java.math.RoundingMode; 32 33 import java.text.DecimalFormat; 33 34 import java.util.HashMap; … … 51 52 @NotNull 52 53 private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#"); 53 @NotNull54 private static final java.text.DecimalFormat SimpleDecimalFormatterWithPrecision = new DecimalFormat("0.0");55 54 56 55 // Note for the potential addition of brightness presentation in eV: … … 206 205 case TAG_JPEG_PROC: 207 206 return getJpegProcDescription(); 207 case TAG_LENS_SPECIFICATION: 208 return getLensSpecificationDescription(); 208 209 default: 209 210 return super.getDescription(tagType); … … 509 510 510 511 @Nullable 512 public String getLensSpecificationDescription() 513 { 514 return getLensSpecificationDescription(TAG_LENS_SPECIFICATION); 515 } 516 517 @Nullable 511 518 public String getSharpnessDescription() 512 519 { … … 568 575 ? null 569 576 : value == 0 570 ? "Unknown"571 : SimpleDecimalFormatter.format(value) + "mm";577 ? "Unknown" 578 : getFocalLengthDescription(value); 572 579 } 573 580 … … 579 586 ? null 580 587 : value.getNumerator() == 0 581 ? "Digital zoom not used."582 : SimpleDecimalFormatter.format(value.doubleValue());588 ? "Digital zoom not used" 589 : SimpleDecimalFormatter.format(value.doubleValue()); 583 590 } 584 591 … … 711 718 return null; 712 719 double fStop = PhotographicConversions.apertureToFStop(aperture); 713 return "f/" + SimpleDecimalFormatterWithPrecision.format(fStop);720 return getFStopDescription(fStop); 714 721 } 715 722 … … 721 728 return null; 722 729 double fStop = PhotographicConversions.apertureToFStop(aperture); 723 return "f/" + SimpleDecimalFormatterWithPrecision.format(fStop);730 return getFStopDescription(fStop); 724 731 } 725 732 … … 807 814 { 808 815 Rational value = _directory.getRational(TAG_FOCAL_LENGTH); 809 if (value == null) 810 return null; 811 java.text.DecimalFormat formatter = new DecimalFormat("0.0##"); 812 return formatter.format(value.doubleValue()) + " mm"; 816 return value == null ? null : getFocalLengthDescription(value.doubleValue()); 813 817 } 814 818 … … 859 863 public String getWhiteBalanceDescription() 860 864 { 861 // '0' means unknown, '1' daylight, '2' fluorescent, '3' tungsten, '4' flash, 862 // '17' standard light A, '18' standard light B, '19' standard light C, '20' D55, 863 // '21' D65, '22' D75, '255' other. 864 // see http://web.archive.org/web/20131018091152/http://exif.org/Exif2-2.PDF page 35 865 // See http://web.archive.org/web/20131018091152/http://exif.org/Exif2-2.PDF page 35 865 866 final Integer value = _directory.getInteger(TAG_WHITE_BALANCE); 866 867 if (value == null) … … 875 876 case 10: return "Cloudy"; 876 877 case 11: return "Shade"; 877 case 12: return "Daylight Fl ourescent";878 case 13: return "Day White Fl ourescent";879 case 14: return "Cool White Fl ourescent";880 case 15: return "White Fl ourescent";881 case 16: return "Warm White Fl ourescent";878 case 12: return "Daylight Fluorescent"; 879 case 13: return "Day White Fluorescent"; 880 case 14: return "Cool White Fluorescent"; 881 case 15: return "White Fluorescent"; 882 case 16: return "Warm White Fluorescent"; 882 883 case 17: return "Standard light"; 883 884 case 18: return "Standard light (B)"; … … 975 976 if (value == null) 976 977 return null; 977 java.text.DecimalFormat formatter = new DecimalFormat("0.0##");978 DecimalFormat formatter = new DecimalFormat("0.0##"); 978 979 return formatter.format(value.doubleValue()) + " metres"; 979 980 } … … 1018 1019 long apexPower10 = Math.round((double)apexPower * 10.0); 1019 1020 float fApexPower = (float)apexPower10 / 10.0f; 1020 return fApexPower + " sec"; 1021 DecimalFormat format = new DecimalFormat("0.##"); 1022 format.setRoundingMode(RoundingMode.HALF_UP); 1023 return format.format(fApexPower) + " sec"; 1021 1024 } else { 1022 1025 int apexPower = (int)((Math.exp(apexValue * Math.log(2)))); … … 1051 1054 if (value == null) 1052 1055 return null; 1053 return "f/" + SimpleDecimalFormatterWithPrecision.format(value.doubleValue());1056 return getFStopDescription(value.doubleValue()); 1054 1057 } 1055 1058 -
trunk/src/com/drew/metadata/exif/ExifDirectoryBase.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 140 140 public static final int TAG_TILE_BYTE_COUNTS = 0x0145; 141 141 142 /** 143 * Tag is a pointer to one or more sub-IFDs. 144 + Seems to be used exclusively by raw formats, referencing one or two IFDs. 145 */ 142 146 public static final int TAG_SUB_IFD_OFFSET = 0x014a; 143 147 … … 150 154 public static final int TAG_YCBCR_POSITIONING = 0x0213; 151 155 public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214; 156 public static final int TAG_STRIP_ROW_COUNTS = 0x022f; 157 public static final int TAG_APPLICATION_NOTES = 0x02bc; 152 158 153 159 public static final int TAG_RELATED_IMAGE_FILE_FORMAT = 0x1000; … … 260 266 public static final int TAG_METERING_MODE = 0x9207; 261 267 262 public static final int TAG_LIGHT_SOURCE = 0x9208; // TODO duplicate tag263 268 /** 264 269 * White balance (aka light source). '0' means unknown, '1' daylight, … … 267 272 * '22' D75, '255' other. 268 273 */ 269 public static final int TAG_WHITE_BALANCE = 0x9208; // TODO duplicate tag274 public static final int TAG_WHITE_BALANCE = 0x9208; 270 275 /** 271 276 * 0x0 = 0000000 = No Flash … … 600 605 map.put(TAG_ROWS_PER_STRIP, "Rows Per Strip"); 601 606 map.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts"); 602 map.put(TAG_MIN_SAMPLE_VALUE, "Minimum sample value");603 map.put(TAG_MAX_SAMPLE_VALUE, "Maximum sample value");607 map.put(TAG_MIN_SAMPLE_VALUE, "Minimum Sample Value"); 608 map.put(TAG_MAX_SAMPLE_VALUE, "Maximum Sample Value"); 604 609 map.put(TAG_X_RESOLUTION, "X Resolution"); 605 610 map.put(TAG_Y_RESOLUTION, "Y Resolution"); … … 627 632 map.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning"); 628 633 map.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White"); 634 map.put(TAG_STRIP_ROW_COUNTS, "Strip Row Counts"); 635 map.put(TAG_APPLICATION_NOTES, "Application Notes"); 629 636 map.put(TAG_RELATED_IMAGE_FILE_FORMAT, "Related Image File Format"); 630 637 map.put(TAG_RELATED_IMAGE_WIDTH, "Related Image Width"); … … 663 670 map.put(TAG_SUBJECT_DISTANCE, "Subject Distance"); 664 671 map.put(TAG_METERING_MODE, "Metering Mode"); 665 map.put(TAG_LIGHT_SOURCE, "Light Source");666 672 map.put(TAG_WHITE_BALANCE, "White Balance"); 667 673 map.put(TAG_FLASH, "Flash"); -
trunk/src/com/drew/metadata/exif/ExifIFD0Descriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifIFD0Directory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 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 java.util.HashMap; 25 24 26 import com.drew.lang.annotations.NotNull; 25 26 import java.util.HashMap;27 27 28 28 /** -
trunk/src/com/drew/metadata/exif/ExifInteropDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifInteropDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 28 28 import com.drew.lang.RandomAccessReader; 29 29 import com.drew.lang.annotations.NotNull; 30 import com.drew.lang.annotations.Nullable; 31 import com.drew.metadata.Directory; 30 32 import com.drew.metadata.Metadata; 31 33 32 34 import java.io.IOException; 33 import java.util. Arrays;35 import java.util.Collections; 34 36 35 37 /** … … 60 62 public Iterable<JpegSegmentType> getSegmentTypes() 61 63 { 62 return Arrays.asList(JpegSegmentType.APP1);64 return Collections.singletonList(JpegSegmentType.APP1); 63 65 } 64 66 … … 84 86 public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset) 85 87 { 88 extract(reader, metadata, readerOffset, null); 89 } 90 91 /** Reads TIFF formatted Exif data a specified offset within a {@link RandomAccessReader}. */ 92 public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset, @Nullable Directory parentDirectory) 93 { 86 94 try { 87 95 // Read the TIFF-formatted Exif data 88 96 new TiffReader().processTiff( 89 97 reader, 90 new ExifTiffHandler(metadata, _storeThumbnailBytes ),98 new ExifTiffHandler(metadata, _storeThumbnailBytes, parentDirectory), 91 99 readerOffset 92 100 ); -
trunk/src/com/drew/metadata/exif/ExifSubIFDDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/ExifSubIFDDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 21 21 package com.drew.metadata.exif; 22 22 23 import java.util.Date; 24 import java.util.HashMap; 25 import java.util.TimeZone; 26 23 27 import com.drew.lang.annotations.NotNull; 24 25 import java.util.HashMap; 28 import com.drew.lang.annotations.Nullable; 26 29 27 30 /** … … 61 64 return _tagNameMap; 62 65 } 66 67 /** 68 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds 69 * representing the date and time when this image was captured. Attempts will be made to parse the 70 * values as though it is in the GMT {@link TimeZone}. 71 * 72 * @return A Date object representing when this image was captured, if possible, otherwise null 73 */ 74 @Nullable 75 public Date getDateOriginal() 76 { 77 return getDateOriginal(null); 78 } 79 80 /** 81 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds 82 * representing the date and time when this image was captured. Attempts will be made to parse the 83 * values as though it is in the {@link TimeZone} represented by the {@code timeZone} parameter 84 * (if it is non-null). 85 * 86 * @param timeZone the time zone to use 87 * @return A Date object representing when this image was captured, if possible, otherwise null 88 */ 89 @Nullable 90 public Date getDateOriginal(TimeZone timeZone) 91 { 92 return getDate(TAG_DATETIME_ORIGINAL, getString(TAG_SUBSECOND_TIME_ORIGINAL), timeZone); 93 } 94 95 /** 96 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds 97 * representing the date and time when this image was digitized. Attempts will be made to parse the 98 * values as though it is in the GMT {@link TimeZone}. 99 * 100 * @return A Date object representing when this image was digitized, if possible, otherwise null 101 */ 102 @Nullable 103 public Date getDateDigitized() 104 { 105 return getDateDigitized(null); 106 } 107 108 /** 109 * Parses the date/time tag and the subsecond tag to obtain a single Date object with milliseconds 110 * representing the date and time when this image was digitized. Attempts will be made to parse the 111 * values as though it is in the {@link TimeZone} represented by the {@code timeZone} parameter 112 * (if it is non-null). 113 * 114 * @param timeZone the time zone to use 115 * @return A Date object representing when this image was digitized, if possible, otherwise null 116 */ 117 @Nullable 118 public Date getDateDigitized(TimeZone timeZone) 119 { 120 return getDate(TAG_DATETIME_DIGITIZED, getString(TAG_SUBSECOND_TIME_DIGITIZED), timeZone); 121 } 63 122 } -
trunk/src/com/drew/metadata/exif/ExifThumbnailDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 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 24 27 import com.drew.lang.annotations.NotNull; 25 28 import com.drew.lang.annotations.Nullable; 26 27 import static com.drew.metadata.exif.ExifThumbnailDirectory.*;28 29 29 30 /** … … 48 49 case TAG_THUMBNAIL_LENGTH: 49 50 return getThumbnailLengthDescription(); 50 case TAG_THUMBNAIL_COMPRESSION:51 return getCompressionDescription();52 51 default: 53 52 return super.getDescription(tagType); 54 }55 }56 57 @Nullable58 public String getCompressionDescription()59 {60 Integer value = _directory.getInteger(TAG_THUMBNAIL_COMPRESSION);61 if (value == null)62 return null;63 switch (value) {64 case 1: return "Uncompressed";65 case 2: return "CCITT 1D";66 case 3: return "T4/Group 3 Fax";67 case 4: return "T6/Group 4 Fax";68 case 5: return "LZW";69 case 6: return "JPEG (old-style)";70 case 7: return "JPEG";71 case 8: return "Adobe Deflate";72 case 9: return "JBIG B&W";73 case 10: return "JBIG Color";74 case 32766: return "Next";75 case 32771: return "CCIRLEW";76 case 32773: return "PackBits";77 case 32809: return "Thunderscan";78 case 32895: return "IT8CTPAD";79 case 32896: return "IT8LW";80 case 32897: return "IT8MP";81 case 32898: return "IT8BL";82 case 32908: return "PixarFilm";83 case 32909: return "PixarLog";84 case 32946: return "Deflate";85 case 32947: return "DCS";86 case 32661: return "JBIG";87 case 32676: return "SGILog";88 case 32677: return "SGILog24";89 case 32712: return "JPEG 2000";90 case 32713: return "Nikon NEF Compressed";91 default:92 return "Unknown compression";93 53 } 94 54 } -
trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 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 java.io.FileOutputStream; 25 import java.io.IOException; 26 import java.util.HashMap; 27 24 28 import com.drew.lang.annotations.NotNull; 25 29 import com.drew.lang.annotations.Nullable; 26 30 import com.drew.metadata.MetadataException; 27 28 import java.io.FileOutputStream;29 import java.io.IOException;30 import java.util.HashMap;31 31 32 32 /** … … 46 46 public static final int TAG_THUMBNAIL_LENGTH = 0x0202; 47 47 48 /**49 * Shows compression method for Thumbnail.50 * 1 = Uncompressed51 * 2 = CCITT 1D52 * 3 = T4/Group 3 Fax53 * 4 = T6/Group 4 Fax54 * 5 = LZW55 * 6 = JPEG (old-style)56 * 7 = JPEG57 * 8 = Adobe Deflate58 * 9 = JBIG B&W59 * 10 = JBIG Color60 * 32766 = Next61 * 32771 = CCIRLEW62 * 32773 = PackBits63 * 32809 = Thunderscan64 * 32895 = IT8CTPAD65 * 32896 = IT8LW66 * 32897 = IT8MP67 * 32898 = IT8BL68 * 32908 = PixarFilm69 * 32909 = PixarLog70 * 32946 = Deflate71 * 32947 = DCS72 * 34661 = JBIG73 * 34676 = SGILog74 * 34677 = SGILog2475 * 34712 = JPEG 200076 * 34713 = Nikon NEF Compressed77 */78 public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103;79 80 48 @NotNull 81 49 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); … … 85 53 addExifTagNames(_tagNameMap); 86 54 87 _tagNameMap.put(TAG_THUMBNAIL_COMPRESSION, "Thumbnail Compression");88 55 _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset"); 89 56 _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length"); -
trunk/src/com/drew/metadata/exif/ExifTiffHandler.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 26 26 import com.drew.lang.SequentialByteArrayReader; 27 27 import com.drew.lang.annotations.NotNull; 28 import com.drew.lang.annotations.Nullable; 28 29 import com.drew.metadata.Directory; 29 30 import com.drew.metadata.Metadata; … … 47 48 private final boolean _storeThumbnailBytes; 48 49 49 public ExifTiffHandler(@NotNull Metadata metadata, boolean storeThumbnailBytes )50 public ExifTiffHandler(@NotNull Metadata metadata, boolean storeThumbnailBytes, @Nullable Directory parentDirectory) 50 51 { 51 52 super(metadata, ExifIFD0Directory.class); 52 53 _storeThumbnailBytes = storeThumbnailBytes; 54 55 if (parentDirectory != null) 56 _currentDirectory.setParent(parentDirectory); 53 57 } 54 58 … … 65 69 } 66 70 67 public boolean isTagIfdPointer(int tagType)68 { 69 if (tag Type == ExifIFD0Directory.TAG_EXIF_SUB_IFD_OFFSET && _currentDirectory instanceof ExifIFD0Directory) {71 public boolean tryEnterSubIfd(int tagId) 72 { 73 if (tagId == ExifDirectoryBase.TAG_SUB_IFD_OFFSET) { 70 74 pushDirectory(ExifSubIFDDirectory.class); 71 75 return true; 72 } else if (tagType == ExifIFD0Directory.TAG_GPS_INFO_OFFSET && _currentDirectory instanceof ExifIFD0Directory) { 73 pushDirectory(GpsDirectory.class); 74 return true; 75 } else if (tagType == ExifSubIFDDirectory.TAG_INTEROP_OFFSET && _currentDirectory instanceof ExifSubIFDDirectory) { 76 pushDirectory(ExifInteropDirectory.class); 77 return true; 76 } 77 78 if (_currentDirectory instanceof ExifIFD0Directory) { 79 if (tagId == ExifIFD0Directory.TAG_EXIF_SUB_IFD_OFFSET) { 80 pushDirectory(ExifSubIFDDirectory.class); 81 return true; 82 } 83 84 if (tagId == ExifIFD0Directory.TAG_GPS_INFO_OFFSET) { 85 pushDirectory(GpsDirectory.class); 86 return true; 87 } 88 } 89 90 if (_currentDirectory instanceof ExifSubIFDDirectory) { 91 if (tagId == ExifSubIFDDirectory.TAG_INTEROP_OFFSET) { 92 pushDirectory(ExifInteropDirectory.class); 93 return true; 94 } 95 } 96 97 if (_currentDirectory instanceof OlympusMakernoteDirectory) { 98 if (tagId == OlympusMakernoteDirectory.TAG_EQUIPMENT) { 99 pushDirectory(OlympusEquipmentMakernoteDirectory.class); 100 return true; 101 } 102 103 if (tagId == OlympusMakernoteDirectory.TAG_CAMERA_SETTINGS) { 104 pushDirectory(OlympusCameraSettingsMakernoteDirectory.class); 105 return true; 106 } 78 107 } 79 108 … … 96 125 // NOTE have seen the CanonMakernoteDirectory IFD have a follower pointer, but it points to invalid data. 97 126 return false; 127 } 128 129 @Nullable 130 public Long tryCustomProcessFormat(final int tagId, final int formatCode, final long componentCount) 131 { 132 if (formatCode == 13) 133 return componentCount * 4; 134 135 return null; 98 136 } 99 137 … … 115 153 if (reader.getInt8(tagOffset) == 0x1c) { 116 154 final byte[] iptcBytes = reader.getBytes(tagOffset, byteCount); 117 new IptcReader().extract(new SequentialByteArrayReader(iptcBytes), _metadata, iptcBytes.length );155 new IptcReader().extract(new SequentialByteArrayReader(iptcBytes), _metadata, iptcBytes.length, _currentDirectory); 118 156 return true; 119 157 } … … 129 167 // after the extraction process, if we have the correct tags, we may be able to store thumbnail information 130 168 ExifThumbnailDirectory thumbnailDirectory = _metadata.getFirstDirectoryOfType(ExifThumbnailDirectory.class); 131 if (thumbnailDirectory != null && thumbnailDirectory.containsTag(ExifThumbnailDirectory.TAG_ THUMBNAIL_COMPRESSION)) {169 if (thumbnailDirectory != null && thumbnailDirectory.containsTag(ExifThumbnailDirectory.TAG_COMPRESSION)) { 132 170 Integer offset = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET); 133 171 Integer length = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH); … … 164 202 final String firstSevenChars = reader.getString(makernoteOffset, 7); 165 203 final String firstEightChars = reader.getString(makernoteOffset, 8); 204 final String firstTenChars = reader.getString(makernoteOffset, 10); 166 205 final String firstTwelveChars = reader.getString(makernoteOffset, 12); 167 206 168 207 boolean byteOrderBefore = reader.isMotorolaByteOrder(); 169 208 170 if ("OLYMP ".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) {209 if ("OLYMP\0".equals(firstSixChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) { 171 210 // Olympus Makernote 172 211 // Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ 173 212 pushDirectory(OlympusMakernoteDirectory.class); 174 213 TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset); 214 } else if ("OLYMPUS\0II".equals(firstTenChars)) { 215 // Olympus Makernote (alternate) 216 // Note that data is relative to the beginning of the makernote 217 // http://exiv2.org/makernote.html 218 pushDirectory(OlympusMakernoteDirectory.class); 219 TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 12, makernoteOffset); 175 220 } else if (cameraMake != null && cameraMake.toUpperCase().startsWith("MINOLTA")) { 176 221 // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote -
trunk/src/com/drew/metadata/exif/GpsDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 110 110 // time in hour, min, sec 111 111 Rational[] timeComponents = _directory.getRationalArray(TAG_TIME_STAMP); 112 DecimalFormat df = new DecimalFormat("00.00 ");112 DecimalFormat df = new DecimalFormat("00.000"); 113 113 return timeComponents == null 114 114 ? null -
trunk/src/com/drew/metadata/exif/GpsDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 26 26 import com.drew.lang.annotations.Nullable; 27 27 28 import java.text.DateFormat; 29 import java.text.ParseException; 30 import java.text.SimpleDateFormat; 31 import java.util.Date; 28 32 import java.util.HashMap; 33 import java.util.Locale; 29 34 30 35 /** … … 164 169 public GeoLocation getGeoLocation() 165 170 { 166 Rational[] latitudes = getRationalArray( GpsDirectory.TAG_LATITUDE);167 Rational[] longitudes = getRationalArray( GpsDirectory.TAG_LONGITUDE);168 String latitudeRef = getString( GpsDirectory.TAG_LATITUDE_REF);169 String longitudeRef = getString( GpsDirectory.TAG_LONGITUDE_REF);171 Rational[] latitudes = getRationalArray(TAG_LATITUDE); 172 Rational[] longitudes = getRationalArray(TAG_LONGITUDE); 173 String latitudeRef = getString(TAG_LATITUDE_REF); 174 String longitudeRef = getString(TAG_LONGITUDE_REF); 170 175 171 176 // Make sure we have the required values … … 186 191 return new GeoLocation(lat, lon); 187 192 } 193 194 /** 195 * Parses the date stamp tag and the time stamp tag to obtain a single Date object representing the 196 * date and time when this image was captured. 197 * 198 * @return A Date object representing when this image was captured, if possible, otherwise null 199 */ 200 @Nullable 201 public Date getGpsDate() 202 { 203 String date = getString(TAG_DATE_STAMP); 204 Rational[] timeComponents = getRationalArray(TAG_TIME_STAMP); 205 206 // Make sure we have the required values 207 if (date == null) 208 return null; 209 if (timeComponents == null || timeComponents.length != 3) 210 return null; 211 212 String dateTime = String.format(Locale.US, "%s %02d:%02d:%02.3f UTC", 213 date, timeComponents[0].intValue(), timeComponents[1].intValue(), timeComponents[2].doubleValue()); 214 try { 215 DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss.S z"); 216 return parser.parse(dateTime); 217 } catch (ParseException e) { 218 return null; 219 } 220 } 188 221 } -
trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 24 24 import com.drew.lang.annotations.Nullable; 25 25 import com.drew.metadata.TagDescriptor; 26 27 import java.text.DecimalFormat; 26 28 27 29 import static com.drew.metadata.exif.makernotes.CanonMakernoteDirectory.*; … … 138 140 public String getSerialNumberDescription() 139 141 { 142 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html 140 143 Integer value = _directory.getInteger(TAG_CANON_SERIAL_NUMBER); 141 144 if (value == null) … … 673 676 return "Self timer not used"; 674 677 } else { 675 // TODO find an image that tests this calculation676 return Double.toString((double)value * 0.1d) + " sec";678 DecimalFormat format = new DecimalFormat("0.##"); 679 return format.format((double)value * 0.1d) + " sec"; 677 680 } 678 681 } -
trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 155 155 { 156 156 Integer value = _directory.getInteger(TAG_OBJECT_DISTANCE); 157 158 if (value == null) 159 return null; 160 161 return value + " mm"; 157 return value == null ? null : getFocalLengthDescription(value); 162 158 } 163 159 -
trunk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 240 240 { 241 241 Double value = _directory.getDoubleObject(TAG_FOCAL_LENGTH); 242 if (value == null) 243 return null; 244 return Double.toString(value / 10d) + " mm"; 242 return value == null ? null : getFocalLengthDescription(value / 10d); 245 243 } 246 244 -
trunk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 306 306 { 307 307 int[] values = _directory.getIntArray(tagType); 308 if (values == null )308 if (values == null || values.length < 2) 309 309 return null; 310 310 if (values.length < 3 || values[2] == 0) … … 329 329 public String getLensDescription() 330 330 { 331 Rational[] values = _directory.getRationalArray(TAG_LENS); 332 333 return values == null 334 ? null 335 : values.length < 4 336 ? _directory.getString(TAG_LENS) 337 : String.format("%d-%dmm f/%.1f-%.1f", values[0].intValue(), values[1].intValue(), values[2].floatValue(), values[3].floatValue()); 338 331 return getLensSpecificationDescription(TAG_LENS); 339 332 } 340 333 -
trunk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 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.DateUtil; 23 24 import com.drew.lang.annotations.NotNull; 24 25 import com.drew.lang.annotations.Nullable; 25 26 import com.drew.metadata.TagDescriptor; 26 27 27 import java.util.GregorianCalendar; 28 import java.math.RoundingMode; 29 import java.text.DecimalFormat; 28 30 29 31 import static com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory.*; … … 115 117 return getFocusDistanceDescription(); 116 118 case CameraSettings.TAG_FLASH_FIRED: 117 return getFlas tFiredDescription();119 return getFlashFiredDescription(); 118 120 case CameraSettings.TAG_DATE: 119 121 return getDateDescription(); … … 142 144 return getSubjectProgramDescription(); 143 145 case CameraSettings.TAG_FLASH_COMPENSATION: 144 return getFlas tCompensationDescription();146 return getFlashCompensationDescription(); 145 147 case CameraSettings.TAG_ISO_SETTING: 146 148 return getIsoSettingDescription(); … … 258 260 259 261 double iso = Math.pow((value / 8d) - 1, 2) * 3.125; 260 return Double.toString(iso); 262 DecimalFormat format = new DecimalFormat("0.##"); 263 format.setRoundingMode(RoundingMode.HALF_UP); 264 return format.format(iso); 261 265 } 262 266 … … 274 278 275 279 double shutterSpeed = Math.pow((49-value) / 8d, 2); 276 return Double.toString(shutterSpeed) + " sec"; 280 DecimalFormat format = new DecimalFormat("0.###"); 281 format.setRoundingMode(RoundingMode.HALF_UP); 282 return format.format(shutterSpeed) + " sec"; 277 283 } 278 284 … … 289 295 290 296 double fStop = Math.pow((value/16d) - 0.5, 2); 291 return "F" + Double.toString(fStop);297 return getFStopDescription(fStop); 292 298 } 293 299 … … 308 314 { 309 315 Long value = _directory.getLongObject(CameraSettings.TAG_EXPOSURE_COMPENSATION); 310 return value == null ? null : ((value / 3d) - 2) + " EV"; 316 DecimalFormat format = new DecimalFormat("0.##"); 317 return value == null ? null : format.format((value / 3d) - 2) + " EV"; 311 318 } 312 319 … … 341 348 { 342 349 Long value = _directory.getLongObject(CameraSettings.TAG_FOCAL_LENGTH); 343 return value == null ? null : Double.toString(value/256d) + " mm";350 return value == null ? null : getFocalLengthDescription(value/256d); 344 351 } 345 352 … … 356 363 357 364 @Nullable 358 public String getFlas tFiredDescription()365 public String getFlashFiredDescription() 359 366 { 360 367 return getIndexedDescription(CameraSettings.TAG_FLASH_FIRED, "No", "Yes"); … … 370 377 if (value == null) 371 378 return null; 372 long day = value & 0xFF; 373 long month = (value >> 16) & 0xFF; 374 long year = (value >> 8) & 0xFF; 375 return new GregorianCalendar((int)year + 1970, (int)month, (int)day).getTime().toString(); 379 380 int day = (int) (value & 0xFF); 381 int month = (int) ((value >> 16) & 0xFF); 382 int year = (int) ((value >> 8) & 0xFF) + 1970; 383 384 if (!DateUtil.isValidDate(year, month, day)) 385 return "Invalid date"; 386 387 return String.format("%04d-%02d-%02d", year, month + 1, day); 376 388 } 377 389 … … 385 397 if (value == null) 386 398 return null; 387 long hours = (value >> 8) & 0xFF; 388 long minutes = (value >> 16) & 0xFF; 389 long seconds = value & 0xFF; 399 400 int hours = (int) ((value >> 8) & 0xFF); 401 int minutes = (int) ((value >> 16) & 0xFF); 402 int seconds = (int) (value & 0xFF); 403 404 if (!DateUtil.isValidTime(hours, minutes, seconds)) 405 return "Invalid time"; 390 406 391 407 return String.format("%02d:%02d:%02d", hours, minutes, seconds); … … 400 416 return null; 401 417 double fStop = Math.pow((value/16d) - 0.5, 2); 402 return "F" + fStop;418 return getFStopDescription(fStop); 403 419 } 404 420 … … 424 440 { 425 441 Long value = _directory.getLongObject(CameraSettings.TAG_WHITE_BALANCE_RED); 426 return value == null ? null : Double.toString(value/256d); 442 DecimalFormat format = new DecimalFormat("0.##"); 443 return value == null ? null : format.format(value/256d); 427 444 } 428 445 … … 431 448 { 432 449 Long value = _directory.getLongObject(CameraSettings.TAG_WHITE_BALANCE_GREEN); 433 return value == null ? null : Double.toString(value/256d); 450 DecimalFormat format = new DecimalFormat("0.##"); 451 return value == null ? null : format.format(value/256d); 434 452 } 435 453 … … 438 456 { 439 457 Long value = _directory.getLongObject(CameraSettings.TAG_WHITE_BALANCE_BLUE); 440 return value == null ? null : Double.toString(value/256d); 458 DecimalFormat format = new DecimalFormat("0.##"); 459 return value == null ? null : format.format(value / 256d); 441 460 } 442 461 … … 468 487 469 488 @Nullable 470 public String getFlas tCompensationDescription()489 public String getFlashCompensationDescription() 471 490 { 472 491 Long value = _directory.getLongObject(CameraSettings.TAG_FLASH_COMPENSATION); 473 return value == null ? null : ((value-6)/3d) + " EV"; 492 DecimalFormat format = new DecimalFormat("0.##"); 493 return value == null ? null : format.format((value-6)/3d) + " EV"; 474 494 } 475 495 … … 535 555 { 536 556 Long value = _directory.getLongObject(CameraSettings.TAG_APEX_BRIGHTNESS_VALUE); 537 return value == null ? null : Double.toString((value/8d)-6); 557 DecimalFormat format = new DecimalFormat("0.##"); 558 return value == null ? null : format.format((value/8d)-6); 538 559 } 539 560 -
trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/makernotes/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/exif/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/file/FileMetadataDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 25 25 import com.drew.metadata.TagDescriptor; 26 26 27 import static com.drew.metadata.file.FileMetadataDirectory.*; 28 27 29 /** 28 30 * @author Drew Noakes https://drewnoakes.com … … 40 42 { 41 43 switch (tagType) { 42 case FileMetadataDirectory.TAG_FILE_SIZE:44 case TAG_FILE_SIZE: 43 45 return getFileSizeDescription(); 44 46 default: … … 50 52 private String getFileSizeDescription() 51 53 { 52 Long size = _directory.getLongObject( FileMetadataDirectory.TAG_FILE_SIZE);54 Long size = _directory.getLongObject(TAG_FILE_SIZE); 53 55 54 56 if (size == null) -
trunk/src/com/drew/metadata/file/FileMetadataDirectory.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/file/FileMetadataReader.java
r8243 r10862 1 /* 2 * Copyright 2002-2016 Drew Noakes 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * https://drewnoakes.com/code/exif/ 19 * https://github.com/drewnoakes/metadata-extractor 20 */ 1 21 package com.drew.metadata.file; 2 22 -
trunk/src/com/drew/metadata/file/package.html
r8243 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/iptc/IptcDescriptor.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 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 static com.drew.metadata.iptc.IptcDirectory.*; 29 28 30 /** 29 31 * Provides human-readable string representations of tag values stored in a {@link IptcDirectory}. … … 45 47 { 46 48 switch (tagType) { 47 case IptcDirectory.TAG_FILE_FORMAT: 49 case TAG_DATE_CREATED: 50 return getDateCreatedDescription(); 51 case TAG_DIGITAL_DATE_CREATED: 52 return getDigitalDateCreatedDescription(); 53 case TAG_DATE_SENT: 54 return getDateSentDescription(); 55 case TAG_EXPIRATION_DATE: 56 return getExpirationDateDescription(); 57 case TAG_EXPIRATION_TIME: 58 return getExpirationTimeDescription(); 59 case TAG_FILE_FORMAT: 48 60 return getFileFormatDescription(); 49 case IptcDirectory.TAG_KEYWORDS:61 case TAG_KEYWORDS: 50 62 return getKeywordsDescription(); 51 case IptcDirectory.TAG_TIME_CREATED: 63 case TAG_REFERENCE_DATE: 64 return getReferenceDateDescription(); 65 case TAG_RELEASE_DATE: 66 return getReleaseDateDescription(); 67 case TAG_RELEASE_TIME: 68 return getReleaseTimeDescription(); 69 case TAG_TIME_CREATED: 52 70 return getTimeCreatedDescription(); 53 case IptcDirectory.TAG_DIGITAL_TIME_CREATED:71 case TAG_DIGITAL_TIME_CREATED: 54 72 return getDigitalTimeCreatedDescription(); 73 case TAG_TIME_SENT: 74 return getTimeSentDescription(); 55 75 default: 56 76 return super.getDescription(tagType); … … 59 79 60 80 @Nullable 81 public String getDateDescription(int tagType) 82 { 83 String s = _directory.getString(tagType); 84 if (s == null) 85 return null; 86 if (s.length() == 8) 87 return s.substring(0, 4) + ':' + s.substring(4, 6) + ':' + s.substring(6); 88 return s; 89 } 90 91 @Nullable 92 public String getTimeDescription(int tagType) 93 { 94 String s = _directory.getString(tagType); 95 if (s == null) 96 return null; 97 if (s.length() == 6 || s.length() == 11) 98 return s.substring(0, 2) + ':' + s.substring(2, 4) + ':' + s.substring(4); 99 return s; 100 } 101 102 @Nullable 61 103 public String getFileFormatDescription() 62 104 { 63 Integer value = _directory.getInteger( IptcDirectory.TAG_FILE_FORMAT);105 Integer value = _directory.getInteger(TAG_FILE_FORMAT); 64 106 if (value == null) 65 107 return null; … … 102 144 public String getByLineDescription() 103 145 { 104 return _directory.getString( IptcDirectory.TAG_BY_LINE);146 return _directory.getString(TAG_BY_LINE); 105 147 } 106 148 … … 108 150 public String getByLineTitleDescription() 109 151 { 110 return _directory.getString( IptcDirectory.TAG_BY_LINE_TITLE);152 return _directory.getString(TAG_BY_LINE_TITLE); 111 153 } 112 154 … … 114 156 public String getCaptionDescription() 115 157 { 116 return _directory.getString( IptcDirectory.TAG_CAPTION);158 return _directory.getString(TAG_CAPTION); 117 159 } 118 160 … … 120 162 public String getCategoryDescription() 121 163 { 122 return _directory.getString( IptcDirectory.TAG_CATEGORY);164 return _directory.getString(TAG_CATEGORY); 123 165 } 124 166 … … 126 168 public String getCityDescription() 127 169 { 128 return _directory.getString( IptcDirectory.TAG_CITY);170 return _directory.getString(TAG_CITY); 129 171 } 130 172 … … 132 174 public String getCopyrightNoticeDescription() 133 175 { 134 return _directory.getString( IptcDirectory.TAG_COPYRIGHT_NOTICE);176 return _directory.getString(TAG_COPYRIGHT_NOTICE); 135 177 } 136 178 … … 138 180 public String getCountryOrPrimaryLocationDescription() 139 181 { 140 return _directory.getString( IptcDirectory.TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME);182 return _directory.getString(TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME); 141 183 } 142 184 … … 144 186 public String getCreditDescription() 145 187 { 146 return _directory.getString( IptcDirectory.TAG_CREDIT);188 return _directory.getString(TAG_CREDIT); 147 189 } 148 190 … … 150 192 public String getDateCreatedDescription() 151 193 { 152 return _directory.getString(IptcDirectory.TAG_DATE_CREATED); 194 return getDateDescription(TAG_DATE_CREATED); 195 } 196 197 @Nullable 198 public String getDigitalDateCreatedDescription() 199 { 200 return getDateDescription(TAG_DIGITAL_DATE_CREATED); 201 } 202 203 @Nullable 204 public String getDateSentDescription() 205 { 206 return getDateDescription(TAG_DATE_SENT); 207 } 208 209 @Nullable 210 public String getExpirationDateDescription() 211 { 212 return getDateDescription(TAG_EXPIRATION_DATE); 213 } 214 215 @Nullable 216 public String getExpirationTimeDescription() 217 { 218 return getTimeDescription(TAG_EXPIRATION_TIME); 153 219 } 154 220 … … 156 222 public String getHeadlineDescription() 157 223 { 158 return _directory.getString( IptcDirectory.TAG_HEADLINE);224 return _directory.getString(TAG_HEADLINE); 159 225 } 160 226 … … 162 228 public String getKeywordsDescription() 163 229 { 164 final String[] keywords = _directory.getStringArray( IptcDirectory.TAG_KEYWORDS);230 final String[] keywords = _directory.getStringArray(TAG_KEYWORDS); 165 231 if (keywords==null) 166 232 return null; … … 171 237 public String getObjectNameDescription() 172 238 { 173 return _directory.getString( IptcDirectory.TAG_OBJECT_NAME);239 return _directory.getString(TAG_OBJECT_NAME); 174 240 } 175 241 … … 177 243 public String getOriginalTransmissionReferenceDescription() 178 244 { 179 return _directory.getString( IptcDirectory.TAG_ORIGINAL_TRANSMISSION_REFERENCE);245 return _directory.getString(TAG_ORIGINAL_TRANSMISSION_REFERENCE); 180 246 } 181 247 … … 183 249 public String getOriginatingProgramDescription() 184 250 { 185 return _directory.getString( IptcDirectory.TAG_ORIGINATING_PROGRAM);251 return _directory.getString(TAG_ORIGINATING_PROGRAM); 186 252 } 187 253 … … 189 255 public String getProvinceOrStateDescription() 190 256 { 191 return _directory.getString( IptcDirectory.TAG_PROVINCE_OR_STATE);257 return _directory.getString(TAG_PROVINCE_OR_STATE); 192 258 } 193 259 … … 195 261 public String getRecordVersionDescription() 196 262 { 197 return _directory.getString(IptcDirectory.TAG_APPLICATION_RECORD_VERSION); 263 return _directory.getString(TAG_APPLICATION_RECORD_VERSION); 264 } 265 266 @Nullable 267 public String getReferenceDateDescription() 268 { 269 return getDateDescription(TAG_REFERENCE_DATE); 198 270 } 199 271 … … 201 273 public String getReleaseDateDescription() 202 274 { 203 return _directory.getString(IptcDirectory.TAG_RELEASE_DATE);275 return getDateDescription(TAG_RELEASE_DATE); 204 276 } 205 277 … … 207 279 public String getReleaseTimeDescription() 208 280 { 209 return _directory.getString(IptcDirectory.TAG_RELEASE_TIME);281 return getTimeDescription(TAG_RELEASE_TIME); 210 282 } 211 283 … … 213 285 public String getSourceDescription() 214 286 { 215 return _directory.getString( IptcDirectory.TAG_SOURCE);287 return _directory.getString(TAG_SOURCE); 216 288 } 217 289 … … 219 291 public String getSpecialInstructionsDescription() 220 292 { 221 return _directory.getString( IptcDirectory.TAG_SPECIAL_INSTRUCTIONS);293 return _directory.getString(TAG_SPECIAL_INSTRUCTIONS); 222 294 } 223 295 … … 225 297 public String getSupplementalCategoriesDescription() 226 298 { 227 return _directory.getString( IptcDirectory.TAG_SUPPLEMENTAL_CATEGORIES);299 return _directory.getString(TAG_SUPPLEMENTAL_CATEGORIES); 228 300 } 229 301 … … 231 303 public String getTimeCreatedDescription() 232 304 { 233 String s = _directory.getString(IptcDirectory.TAG_TIME_CREATED); 234 if (s == null) 235 return null; 236 if (s.length() == 6 || s.length() == 11) 237 return s.substring(0, 2) + ':' + s.substring(2, 4) + ':' + s.substring(4); 238 return s; 305 return getTimeDescription(TAG_TIME_CREATED); 239 306 } 240 307 … … 242 309 public String getDigitalTimeCreatedDescription() 243 310 { 244 String s = _directory.getString(IptcDirectory.TAG_DIGITAL_TIME_CREATED); 245 if (s == null) 246 return null; 247 if (s.length() == 6 || s.length() == 11) 248 return s.substring(0, 2) + ':' + s.substring(2, 4) + ':' + s.substring(4); 249 return s; 311 return getTimeDescription(TAG_DIGITAL_TIME_CREATED); 312 } 313 314 @Nullable 315 public String getTimeSentDescription() 316 { 317 return getTimeDescription(TAG_TIME_SENT); 250 318 } 251 319 … … 253 321 public String getUrgencyDescription() 254 322 { 255 return _directory.getString( IptcDirectory.TAG_URGENCY);323 return _directory.getString(TAG_URGENCY); 256 324 } 257 325 … … 259 327 public String getWriterDescription() 260 328 { 261 return _directory.getString( IptcDirectory.TAG_CAPTION_WRITER);329 return _directory.getString(TAG_CAPTION_WRITER); 262 330 } 263 331 } -
trunk/src/com/drew/metadata/iptc/IptcDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 25 25 import com.drew.metadata.Directory; 26 26 27 import java.text.DateFormat; 28 import java.text.ParseException; 29 import java.text.SimpleDateFormat; 27 30 import java.util.Arrays; 31 import java.util.Date; 28 32 import java.util.HashMap; 29 33 import java.util.List; … … 231 235 public List<String> getKeywords() 232 236 { 233 final String[] array = getStringArray( IptcDirectory.TAG_KEYWORDS);237 final String[] array = getStringArray(TAG_KEYWORDS); 234 238 if (array==null) 235 239 return null; 236 240 return Arrays.asList(array); 237 241 } 242 243 /** 244 * Parses the Date Sent tag and the Time Sent tag to obtain a single Date object representing the 245 * date and time when the service sent this image. 246 * @return A Date object representing when the service sent this image, if possible, otherwise null 247 */ 248 @Nullable 249 public Date getDateSent() 250 { 251 return getDate(TAG_DATE_SENT, TAG_TIME_SENT); 252 } 253 254 /** 255 * Parses the Release Date tag and the Release Time tag to obtain a single Date object representing the 256 * date and time when this image was released. 257 * @return A Date object representing when this image was released, if possible, otherwise null 258 */ 259 @Nullable 260 public Date getReleaseDate() 261 { 262 return getDate(TAG_RELEASE_DATE, TAG_RELEASE_TIME); 263 } 264 265 /** 266 * Parses the Expiration Date tag and the Expiration Time tag to obtain a single Date object representing 267 * that this image should not used after this date and time. 268 * @return A Date object representing when this image was released, if possible, otherwise null 269 */ 270 @Nullable 271 public Date getExpirationDate() 272 { 273 return getDate(TAG_EXPIRATION_DATE, TAG_EXPIRATION_TIME); 274 } 275 276 /** 277 * Parses the Date Created tag and the Time Created tag to obtain a single Date object representing the 278 * date and time when this image was captured. 279 * @return A Date object representing when this image was captured, if possible, otherwise null 280 */ 281 @Nullable 282 public Date getDateCreated() 283 { 284 return getDate(TAG_DATE_CREATED, TAG_TIME_CREATED); 285 } 286 287 /** 288 * Parses the Digital Date Created tag and the Digital Time Created tag to obtain a single Date object 289 * representing the date and time when the digital representation of this image was created. 290 * @return A Date object representing when the digital representation of this image was created, 291 * if possible, otherwise null 292 */ 293 @Nullable 294 public Date getDigitalDateCreated() 295 { 296 return getDate(TAG_DIGITAL_DATE_CREATED, TAG_DIGITAL_TIME_CREATED); 297 } 298 299 @Nullable 300 private Date getDate(int dateTagType, int timeTagType) 301 { 302 String date = getString(dateTagType); 303 String time = getString(timeTagType); 304 305 if (date == null) 306 return null; 307 if (time == null) 308 return null; 309 310 try { 311 DateFormat parser = new SimpleDateFormat("yyyyMMddHHmmssZ"); 312 return parser.parse(date + time); 313 } catch (ParseException e) { 314 return null; 315 } 316 } 238 317 } -
trunk/src/com/drew/metadata/iptc/IptcReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 26 26 import com.drew.lang.SequentialReader; 27 27 import com.drew.lang.annotations.NotNull; 28 import com.drew.lang.annotations.Nullable; 28 29 import com.drew.metadata.Directory; 29 30 import com.drew.metadata.Metadata; 30 31 31 32 import java.io.IOException; 32 import java.util.Arrays; 33 import java.util.Date; 33 import java.util.Collections; 34 34 35 35 /** … … 60 60 public Iterable<JpegSegmentType> getSegmentTypes() 61 61 { 62 return Arrays.asList(JpegSegmentType.APPD);62 return Collections.singletonList(JpegSegmentType.APPD); 63 63 } 64 64 … … 78 78 public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length) 79 79 { 80 extract(reader, metadata, length, null); 81 } 82 83 /** 84 * Performs the IPTC data extraction, adding found values to the specified instance of {@link Metadata}. 85 */ 86 public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length, @Nullable Directory parentDirectory) 87 { 80 88 IptcDirectory directory = new IptcDirectory(); 81 89 metadata.addDirectory(directory); 90 91 if (parentDirectory != null) 92 directory.setParent(parentDirectory); 82 93 83 94 int offset = 0; … … 105 116 106 117 // we need at least five bytes left to read a tag 107 if (offset + 5 > =length) {118 if (offset + 5 > length) { 108 119 directory.addError("Too few bytes remain for a valid IPTC tag"); 109 120 return; … … 184 195 reader.skip(tagByteCount - 1); 185 196 return; 186 case IptcDirectory.TAG_RELEASE_DATE:187 case IptcDirectory.TAG_DATE_CREATED:188 // Date object189 if (tagByteCount >= 8) {190 string = reader.getString(tagByteCount);191 try {192 int year = Integer.parseInt(string.substring(0, 4));193 int month = Integer.parseInt(string.substring(4, 6)) - 1;194 int day = Integer.parseInt(string.substring(6, 8));195 Date date = new java.util.GregorianCalendar(year, month, day).getTime();196 directory.setDate(tagIdentifier, date);197 return;198 } catch (NumberFormatException e) {199 // fall through and we'll process the 'string' value below200 }201 } else {202 reader.skip(tagByteCount);203 }204 case IptcDirectory.TAG_RELEASE_TIME:205 case IptcDirectory.TAG_TIME_CREATED:206 // time...207 197 default: 208 198 // fall through … … 227 217 String[] newStrings; 228 218 if (oldStrings == null) { 219 // TODO hitting this block means any prior value(s) are discarded 229 220 newStrings = new String[1]; 230 221 } else { -
trunk/src/com/drew/metadata/iptc/Iso2022Converter.java
r8132 r10862 1 /* 2 * Copyright 2002-2016 Drew Noakes 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * https://drewnoakes.com/code/exif/ 19 * https://github.com/drewnoakes/metadata-extractor 20 */ 1 21 package com.drew.metadata.iptc; 2 22 … … 40 60 /** 41 61 * Attempts to guess the encoding of a string provided as a byte array. 42 * <p />62 * <p> 43 63 * Encodings trialled are, in order: 44 64 * <ul> … … 47 67 * <li>ISO-8859-1</li> 48 68 * </ul> 49 * <p />69 * <p> 50 70 * Its only purpose is to guess the encoding if and only if iptc tag coded character set is not set. If the 51 71 * encoding is not UTF-8, the tag should be set. Otherwise it is bad practice. This method tries to 52 72 * workaround this issue since some metadata manipulating tools do not prevent such bad practice. 53 * <p />73 * <p> 54 74 * About the reliability of this method: The check if some bytes are UTF-8 or not has a very high reliability. 55 75 * The two other checks are less reliable. -
trunk/src/com/drew/metadata/iptc/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegCommentDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegCommentDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegCommentReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 26 26 import com.drew.metadata.Metadata; 27 27 28 import java.util. Arrays;28 import java.util.Collections; 29 29 30 30 /** … … 39 39 public Iterable<JpegSegmentType> getSegmentTypes() 40 40 { 41 return Arrays.asList(JpegSegmentType.COM); 42 } 43 44 public boolean canProcess(@NotNull byte[] segmentBytes, @NotNull JpegSegmentType segmentType) 45 { 46 // The entire contents of the byte[] is the comment. There's nothing here to discriminate upon. 47 return true; 41 return Collections.singletonList(JpegSegmentType.COM); 48 42 } 49 43 -
trunk/src/com/drew/metadata/jpeg/JpegComponent.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 21 21 package com.drew.metadata.jpeg; 22 22 23 import com.drew.lang.annotations.N ullable;23 import com.drew.lang.annotations.NotNull; 24 24 25 25 import java.io.Serializable; … … 55 55 * @return the component name 56 56 */ 57 @N ullable57 @NotNull 58 58 public String getComponentName() 59 59 { … … 70 70 case 5: 71 71 return "Q"; 72 default: 73 return String.format("Unknown (%s)", _componentId); 72 74 } 73 return null;74 75 } 75 76 … … 81 82 public int getHorizontalSamplingFactor() 82 83 { 83 return _samplingFactorByte& 0x0F;84 return (_samplingFactorByte>>4) & 0x0F; 84 85 } 85 86 86 87 public int getVerticalSamplingFactor() 87 88 { 88 return (_samplingFactorByte>>4)& 0x0F;89 return _samplingFactorByte & 0x0F; 89 90 } 90 91 } -
trunk/src/com/drew/metadata/jpeg/JpegDescriptor.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 25 25 import com.drew.metadata.TagDescriptor; 26 26 27 import static com.drew.metadata.jpeg.JpegDirectory.*; 28 27 29 /** 28 30 * Provides human-readable string versions of the tags stored in a JpegDirectory. … … 44 46 switch (tagType) 45 47 { 46 case JpegDirectory.TAG_COMPRESSION_TYPE:48 case TAG_COMPRESSION_TYPE: 47 49 return getImageCompressionTypeDescription(); 48 case JpegDirectory.TAG_COMPONENT_DATA_1:50 case TAG_COMPONENT_DATA_1: 49 51 return getComponentDataDescription(0); 50 case JpegDirectory.TAG_COMPONENT_DATA_2:52 case TAG_COMPONENT_DATA_2: 51 53 return getComponentDataDescription(1); 52 case JpegDirectory.TAG_COMPONENT_DATA_3:54 case TAG_COMPONENT_DATA_3: 53 55 return getComponentDataDescription(2); 54 case JpegDirectory.TAG_COMPONENT_DATA_4:56 case TAG_COMPONENT_DATA_4: 55 57 return getComponentDataDescription(3); 56 case JpegDirectory.TAG_DATA_PRECISION:58 case TAG_DATA_PRECISION: 57 59 return getDataPrecisionDescription(); 58 case JpegDirectory.TAG_IMAGE_HEIGHT:60 case TAG_IMAGE_HEIGHT: 59 61 return getImageHeightDescription(); 60 case JpegDirectory.TAG_IMAGE_WIDTH:62 case TAG_IMAGE_WIDTH: 61 63 return getImageWidthDescription(); 62 64 default: … … 68 70 public String getImageCompressionTypeDescription() 69 71 { 70 Integer value = _directory.getInteger(JpegDirectory.TAG_COMPRESSION_TYPE); 71 if (value==null) 72 return null; 73 // Note there is no 2 or 12 74 switch (value) { 75 case 0: return "Baseline"; 76 case 1: return "Extended sequential, Huffman"; 77 case 2: return "Progressive, Huffman"; 78 case 3: return "Lossless, Huffman"; 79 case 5: return "Differential sequential, Huffman"; 80 case 6: return "Differential progressive, Huffman"; 81 case 7: return "Differential lossless, Huffman"; 82 case 8: return "Reserved for JPEG extensions"; 83 case 9: return "Extended sequential, arithmetic"; 84 case 10: return "Progressive, arithmetic"; 85 case 11: return "Lossless, arithmetic"; 86 case 13: return "Differential sequential, arithmetic"; 87 case 14: return "Differential progressive, arithmetic"; 88 case 15: return "Differential lossless, arithmetic"; 89 default: 90 return "Unknown type: "+ value; 91 } 72 return getIndexedDescription(TAG_COMPRESSION_TYPE, 73 "Baseline", 74 "Extended sequential, Huffman", 75 "Progressive, Huffman", 76 "Lossless, Huffman", 77 null, // no 4 78 "Differential sequential, Huffman", 79 "Differential progressive, Huffman", 80 "Differential lossless, Huffman", 81 "Reserved for JPEG extensions", 82 "Extended sequential, arithmetic", 83 "Progressive, arithmetic", 84 "Lossless, arithmetic", 85 null, // no 12 86 "Differential sequential, arithmetic", 87 "Differential progressive, arithmetic", 88 "Differential lossless, arithmetic"); 92 89 } 90 93 91 @Nullable 94 92 public String getImageWidthDescription() 95 93 { 96 final String value = _directory.getString( JpegDirectory.TAG_IMAGE_WIDTH);94 final String value = _directory.getString(TAG_IMAGE_WIDTH); 97 95 if (value==null) 98 96 return null; … … 103 101 public String getImageHeightDescription() 104 102 { 105 final String value = _directory.getString( JpegDirectory.TAG_IMAGE_HEIGHT);103 final String value = _directory.getString(TAG_IMAGE_HEIGHT); 106 104 if (value==null) 107 105 return null; … … 112 110 public String getDataPrecisionDescription() 113 111 { 114 final String value = _directory.getString( JpegDirectory.TAG_DATA_PRECISION);112 final String value = _directory.getString(TAG_DATA_PRECISION); 115 113 if (value==null) 116 114 return null; -
trunk/src/com/drew/metadata/jpeg/JpegDirectory.java
r8132 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/JpegReader.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/jpeg/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); -
trunk/src/com/drew/metadata/tiff/DirectoryTiffHandler.java
r8243 r10862 1 1 /* 2 * Copyright 2002-201 5Drew Noakes2 * Copyright 2002-2016 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 63 63 _directoryStack.push(_currentDirectory); 64 64 try { 65 _currentDirectory = directoryClass.newInstance(); 65 Directory newDirectory = directoryClass.newInstance(); 66 newDirectory.setParent(_currentDirectory); 67 _currentDirectory = newDirectory; 66 68 } catch (InstantiationException e) { 67 69 throw new RuntimeException(e); -
trunk/src/com/drew/metadata/tiff/package.html
r8132 r10862 1 1 <!-- 2 ~ Copyright 2002-201 5Drew Noakes2 ~ Copyright 2002-2016 Drew Noakes 3 3 ~ 4 4 ~ Licensed under the Apache License, Version 2.0 (the "License");
Note:
See TracChangeset
for help on using the changeset viewer.