diff --git a/Source/com/drew/imaging/jpeg/JpegMetadataReader.java b/Source/com/drew/imaging/jpeg/JpegMetadataReader.java index 4d92f09a7..0a82725ee 100644 --- a/Source/com/drew/imaging/jpeg/JpegMetadataReader.java +++ b/Source/com/drew/imaging/jpeg/JpegMetadataReader.java @@ -24,6 +24,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.adobe.AdobeJpegReader; import com.drew.metadata.exif.ExifReader; import com.drew.metadata.file.FileSystemMetadataReader; @@ -71,45 +72,80 @@ public class JpegMetadataReader ); @NotNull - public static Metadata readMetadata(@NotNull InputStream inputStream, @Nullable Iterable readers) throws JpegProcessingException, IOException + public static Metadata readMetadata(@NotNull InputStream inputStream, @Nullable Iterable readers, @NotNull MetadataContext context) throws JpegProcessingException, IOException { Metadata metadata = new Metadata(); - process(metadata, inputStream, readers); + process(metadata, inputStream, readers, context); return metadata; } + @NotNull + public static Metadata readMetadata(@NotNull InputStream inputStream, @Nullable Iterable readers) throws JpegProcessingException, IOException + { + return readMetadata(inputStream, readers, null); + } + + @NotNull + public static Metadata readMetadata(@NotNull InputStream inputStream, @NotNull MetadataContext context) throws JpegProcessingException, IOException + { + return readMetadata(inputStream, null, context); + } + @NotNull public static Metadata readMetadata(@NotNull InputStream inputStream) throws JpegProcessingException, IOException { - return readMetadata(inputStream, null); + // TODO document this default context? + return readMetadata(inputStream, null, new MetadataContext()); } @NotNull - public static Metadata readMetadata(@NotNull File file, @Nullable Iterable readers) throws JpegProcessingException, IOException + public static Metadata readMetadata(@NotNull File file, @Nullable Iterable readers, @NotNull MetadataContext context) throws JpegProcessingException, IOException { InputStream inputStream = new FileInputStream(file); Metadata metadata; try { - metadata = readMetadata(inputStream, readers); + metadata = readMetadata(inputStream, readers, context); } finally { inputStream.close(); } + // TODO pass locale? new FileSystemMetadataReader().read(file, metadata); return metadata; } + @NotNull + public static Metadata readMetadata(@NotNull File file, @Nullable Iterable readers) throws JpegProcessingException, IOException + { + // TODO document this default context? + return readMetadata(file, readers, new MetadataContext()); + } + + @NotNull + public static Metadata readMetadata(@NotNull File file, @NotNull MetadataContext context) throws JpegProcessingException, IOException + { + return readMetadata(file, null, context); + } + @NotNull public static Metadata readMetadata(@NotNull File file) throws JpegProcessingException, IOException { - return readMetadata(file, null); + // TODO document this default context? + return readMetadata(file, null, new MetadataContext()); } public static void process(@NotNull Metadata metadata, @NotNull InputStream inputStream) throws JpegProcessingException, IOException { - process(metadata, inputStream, null); + // TODO document this default context? + process(metadata, inputStream, null, new MetadataContext()); } public static void process(@NotNull Metadata metadata, @NotNull InputStream inputStream, @Nullable Iterable readers) throws JpegProcessingException, IOException + { + // TODO document this default context? + process(metadata, inputStream, readers, new MetadataContext()); + } + + public static void process(@NotNull Metadata metadata, @NotNull InputStream inputStream, @Nullable Iterable readers, @NotNull MetadataContext context) throws JpegProcessingException, IOException { if (readers == null) readers = ALL_READERS; @@ -123,15 +159,16 @@ public static void process(@NotNull Metadata metadata, @NotNull InputStream inpu JpegSegmentData segmentData = JpegSegmentReader.readSegments(new StreamReader(inputStream), segmentTypes); - processJpegSegmentData(metadata, readers, segmentData); + processJpegSegmentData(metadata, readers, segmentData, context); } - public static void processJpegSegmentData(Metadata metadata, Iterable readers, JpegSegmentData segmentData) + // TODO create method with original signature for backwards compatibility + public static void processJpegSegmentData(@NotNull Metadata metadata, @NotNull Iterable readers, @NotNull JpegSegmentData segmentData, @NotNull MetadataContext context) { // Pass the appropriate byte arrays to each reader. for (JpegSegmentMetadataReader reader : readers) { for (JpegSegmentType segmentType : reader.getSegmentTypes()) { - reader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType); + reader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType, context); } } } diff --git a/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java b/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java index e1d2aef34..f6f27d703 100644 --- a/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java +++ b/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java @@ -2,6 +2,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; /** * Defines an object that extracts metadata from in JPEG segments. @@ -21,6 +22,7 @@ public interface JpegSegmentMetadataReader * encountered in the original file. * @param metadata The {@link Metadata} object into which extracted values should be merged. * @param segmentType The {@link JpegSegmentType} being read. + * @param context The {@link MetadataContext} to use for parsing and formatting. */ - void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType); + void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType, @NotNull MetadataContext context); } diff --git a/Source/com/drew/lang/GeoLocation.java b/Source/com/drew/lang/GeoLocation.java index b4a1abff1..d264cf140 100644 --- a/Source/com/drew/lang/GeoLocation.java +++ b/Source/com/drew/lang/GeoLocation.java @@ -25,6 +25,8 @@ import com.drew.lang.annotations.Nullable; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; /** * Represents a latitude and longitude pair, giving a position on earth in spherical coordinates. @@ -37,17 +39,20 @@ public final class GeoLocation { private final double _latitude; private final double _longitude; + private final Locale _locale; /** * Instantiates a new instance of {@link GeoLocation}. * * @param latitude the latitude, in degrees * @param longitude the longitude, in degrees + * @param locale the locale to use */ - public GeoLocation(double latitude, double longitude) + public GeoLocation(double latitude, double longitude, Locale locale) { _latitude = latitude; _longitude = longitude; + _locale = locale; } /** @@ -77,12 +82,26 @@ public boolean isZero() /** * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) representation as a string, * of format: {@code -1° 23' 4.56"} + * + * @deprecated Use {@link #decimalToDegreesMinutesSecondsString(double, Locale)} instead */ @NotNull + @Deprecated public static String decimalToDegreesMinutesSecondsString(double decimal) + { + return decimalToDegreesMinutesSecondsString(decimal, null); + } + + /** + * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) representation as a string, + * of format: {@code -1° 23' 4.56"} + */ + @NotNull + public static String decimalToDegreesMinutesSecondsString(double decimal, Locale locale) { double[] dms = decimalToDegreesMinutesSeconds(decimal); - DecimalFormat format = new DecimalFormat("0.##"); + DecimalFormatSymbols symbols = locale == null ? DecimalFormatSymbols.getInstance() : DecimalFormatSymbols.getInstance(locale); + DecimalFormat format = new DecimalFormat("0.##", symbols); return String.format("%s\u00B0 %s' %s\"", format.format(dms[0]), format.format(dms[1]), format.format(dms[2])); } @@ -158,6 +177,8 @@ public String toString() @NotNull public String toDMSString() { - return decimalToDegreesMinutesSecondsString(_latitude) + ", " + decimalToDegreesMinutesSecondsString(_longitude); + return decimalToDegreesMinutesSecondsString(_latitude, _locale) + + ", " + + decimalToDegreesMinutesSecondsString(_longitude, _locale); } } diff --git a/Source/com/drew/metadata/Directory.java b/Source/com/drew/metadata/Directory.java index b8e9d7eb8..a8468ce0c 100644 --- a/Source/com/drew/metadata/Directory.java +++ b/Source/com/drew/metadata/Directory.java @@ -29,6 +29,7 @@ import java.lang.reflect.Array; import java.text.DateFormat; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -192,6 +193,12 @@ public void setParent(@NotNull Directory parent) _parent = parent; } + protected Locale getLocale() + { + // TODO discuss: is it acceptable to use a default here? + return _descriptor != null ? _descriptor.getContext().locale() : new MetadataContext().locale(); + } + // TAG SETTERS /** @@ -1024,7 +1031,7 @@ public String getString(int tagType) string.append(Array.getLong(o, i)); } } else if (componentType.getName().equals("float")) { - DecimalFormat format = new DecimalFormat(_floatFormatPattern); + DecimalFormat format = getFloatFormat(); for (int i = 0; i < arrayLength; i++) { if (i != 0) string.append(' '); @@ -1032,7 +1039,7 @@ public String getString(int tagType) string.append(s.equals("-0") ? "0" : s); } } else if (componentType.getName().equals("double")) { - DecimalFormat format = new DecimalFormat(_floatFormatPattern); + DecimalFormat format = getFloatFormat(); for (int i = 0; i < arrayLength; i++) { if (i != 0) string.append(' '); @@ -1053,10 +1060,10 @@ public String getString(int tagType) } if (o instanceof Double) - return new DecimalFormat(_floatFormatPattern).format(((Double)o).doubleValue()); + return getFloatFormat().format(((Double) o).doubleValue()); if (o instanceof Float) - return new DecimalFormat(_floatFormatPattern).format(((Float)o).floatValue()); + return getFloatFormat().format(((Float) o).floatValue()); // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show // the actual data within the file. It is not inconceivable that whitespace may be significant here, so we @@ -1065,6 +1072,12 @@ public String getString(int tagType) return o.toString(); } + private DecimalFormat getFloatFormat() + { + DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(getLocale()); + return new DecimalFormat(_floatFormatPattern, symbols); + } + @Nullable public String getString(int tagType, String charset) { @@ -1150,7 +1163,8 @@ public String getDescription(int tagType) @Override public String toString() { - return String.format("%s Directory (%d %s)", + return String.format(getLocale(), + "%s Directory (%d %s)", getName(), _tagMap.size(), _tagMap.size() == 1 diff --git a/Source/com/drew/metadata/MetadataContext.java b/Source/com/drew/metadata/MetadataContext.java new file mode 100644 index 000000000..5c6fd1c29 --- /dev/null +++ b/Source/com/drew/metadata/MetadataContext.java @@ -0,0 +1,49 @@ +package com.drew.metadata; + +import com.drew.lang.annotations.NotNull; + +import java.util.Locale; + +/** + * Context class for metadata. Contains settings used while extracting and formatting metadata. + * + * @author Roel van Dijk + */ +public class MetadataContext +{ + /** + * Locale used to format metadata. + */ + private Locale _locale; + + /** + * Initialize a context using the system default {@link Locale}. + */ + public MetadataContext() + { + _locale = Locale.getDefault(); + } + + /** + * Gets the configured {@link Locale}. + * + * @return the configured locale. + */ + public Locale locale() + { + return _locale; + } + + /** + * Configure the {@link Locale} to use for extracting and formatting metadata. + * + * @param locale the locale to use + * @return this context + */ + public MetadataContext locale(@NotNull final Locale locale) + { + _locale = locale; + return this; + } + +} diff --git a/Source/com/drew/metadata/TagDescriptor.java b/Source/com/drew/metadata/TagDescriptor.java index a3fa1cd2f..2c17ea212 100644 --- a/Source/com/drew/metadata/TagDescriptor.java +++ b/Source/com/drew/metadata/TagDescriptor.java @@ -30,11 +30,13 @@ import java.math.RoundingMode; import java.nio.charset.Charset; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; /** @@ -48,10 +50,20 @@ public class TagDescriptor { @NotNull protected final T _directory; + @NotNull + protected final MetadataContext _context; + // TODO remove constructor once all sub-classes actually use MetadataContext? public TagDescriptor(@NotNull T directory) + { + // TODO discuss: acceptable to use default here? + this(directory, new MetadataContext()); + } + + public TagDescriptor(@NotNull T directory, @NotNull MetadataContext context) { _directory = directory; + _context = context; } /** @@ -76,13 +88,13 @@ public String getDescription(int tagType) if (object.getClass().isArray()) { final int length = Array.getLength(object); if (length > 16) { - return String.format("[%d values]", length); + return String.format(getContext().locale(), "[%d values]", length); } } if (object instanceof Date) { // Produce a date string having a format that includes the offset in form "+00:00" - return new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy") + return new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", getContext().locale()) .format((Date) object) .replaceAll("([0-9]{2} [^ ]+)$", ":$1"); } @@ -91,6 +103,17 @@ public String getDescription(int tagType) return _directory.getString(tagType); } + /** + * Gets the {@link MetadataContext}. + * + * @return the metadata context. + */ + @NotNull + public MetadataContext getContext() + { + return _context; + } + /** * Takes a series of 4 bytes from the specified offset, and converts these to a * well-known version number, where possible. @@ -158,7 +181,7 @@ protected String getByteLengthDescription(final int tagType) byte[] bytes = _directory.getByteArray(tagType); if (bytes == null) return null; - return String.format("(%d byte%s)", bytes.length, bytes.length == 1 ? "" : "s"); + return String.format(getContext().locale(), "(%d byte%s)", bytes.length, bytes.length == 1 ? "" : "s"); } @Nullable @@ -176,7 +199,7 @@ protected String getDecimalRational(final int tagType, final int decimalPlaces) Rational value = _directory.getRational(tagType); if (value == null) return null; - return String.format("%." + decimalPlaces + "f", value.doubleValue()); + return String.format(getContext().locale(), "%." + decimalPlaces + "f", value.doubleValue()); } @Nullable @@ -185,7 +208,7 @@ protected String getFormattedInt(final int tagType, @NotNull final String format Integer value = _directory.getInteger(tagType); if (value == null) return null; - return String.format(format, value); + return String.format(getContext().locale(), format, value); } @Nullable @@ -194,7 +217,7 @@ protected String getFormattedFloat(final int tagType, @NotNull final String form Float value = _directory.getFloatObject(tagType); if (value == null) return null; - return String.format(format, value); + return String.format(getContext().locale(), format, value); } @Nullable @@ -203,7 +226,7 @@ protected String getFormattedString(final int tagType, @NotNull final String for String value = _directory.getString(tagType); if (value == null) return null; - return String.format(format, value); + return String.format(getContext().locale(), format, value); } @Nullable @@ -284,8 +307,18 @@ protected String getStringFromBytes(int tag, Charset cs) } } + @Deprecated @Nullable + /** + * @deprecated Use {@link #getRationalOrDoubleString(int, Locale)}. + */ protected String getRationalOrDoubleString(int tagType) + { + return getRationalOrDoubleString(tagType, null); + } + + @Nullable + protected String getRationalOrDoubleString(int tagType, Locale locale) { Rational rational = _directory.getRational(tagType); if (rational != null) @@ -293,31 +326,61 @@ protected String getRationalOrDoubleString(int tagType) Double d = _directory.getDoubleObject(tagType); if (d != null) { - DecimalFormat format = new DecimalFormat("0.###"); + DecimalFormat format = new DecimalFormat("0.###", getDecimalFormatSymbols(locale)); return format.format(d); } return null; } + @Deprecated @Nullable + /** + * @deprecated Use {@link #getFStopDescription(double, Locale)}. + */ protected static String getFStopDescription(double fStop) { - DecimalFormat format = new DecimalFormat("0.0"); + return getFStopDescription(fStop, null); + } + + @Nullable + protected static String getFStopDescription(double fStop, Locale locale) + { + DecimalFormat format = new DecimalFormat("0.0", getDecimalFormatSymbols(locale)); format.setRoundingMode(RoundingMode.HALF_UP); return "f/" + format.format(fStop); } + @Deprecated @Nullable + /** + * @deprecated Use {@link #getFocalLengthDescription(double, Locale)}. + */ protected static String getFocalLengthDescription(double mm) { - DecimalFormat format = new DecimalFormat("0.#"); + return getFocalLengthDescription(mm, null); + } + + @Nullable + protected static String getFocalLengthDescription(double mm, Locale locale) + { + DecimalFormat format = new DecimalFormat("0.#", getDecimalFormatSymbols(locale)); format.setRoundingMode(RoundingMode.HALF_UP); return format.format(mm) + " mm"; } + @Deprecated @Nullable + /** + * @deprecated Use {@link #getLensSpecificationDescription(int, Locale)}. + */ protected String getLensSpecificationDescription(int tag) + { + return getLensSpecificationDescription(tag, null); + } + + @Nullable + protected String getLensSpecificationDescription(int tag, Locale locale) { Rational[] values = _directory.getRationalArray(tag); @@ -334,11 +397,11 @@ protected String getLensSpecificationDescription(int tag) if (!values[2].isZero()) { sb.append(' '); - DecimalFormat format = new DecimalFormat("0.0"); + DecimalFormat format = new DecimalFormat("0.0", getDecimalFormatSymbols(locale)); format.setRoundingMode(RoundingMode.HALF_UP); if (values[2].equals(values[3])) - sb.append(getFStopDescription(values[2].doubleValue())); + sb.append(getFStopDescription(values[2].doubleValue(), getContext().locale())); else sb.append("f/").append(format.format(values[2].doubleValue())).append('-').append(format.format(values[3].doubleValue())); } @@ -360,8 +423,18 @@ protected String getOrientationDescription(int tag) "Left side, bottom (Rotate 270 CW)"); } + @Deprecated @Nullable + /** + * @deprecated Use {@link #getShutterSpeedDescription(int, Locale)}. + */ protected String getShutterSpeedDescription(int tag) + { + return getShutterSpeedDescription(tag, null); + } + + @Nullable + protected String getShutterSpeedDescription(int tag, Locale locale) { // I believe this method to now be stable, but am leaving some alternative snippets of // code in here, to assist anyone who's looking into this (given that I don't have a public CVS). @@ -380,7 +453,7 @@ protected String getShutterSpeedDescription(int tag) float apexPower = (float)(1 / (Math.exp(apexValue * Math.log(2)))); long apexPower10 = Math.round((double)apexPower * 10.0); float fApexPower = (float)apexPower10 / 10.0f; - DecimalFormat format = new DecimalFormat("0.##"); + DecimalFormat format = new DecimalFormat("0.##", getDecimalFormatSymbols(locale)); format.setRoundingMode(RoundingMode.HALF_UP); return format.format(fApexPower) + " sec"; } else { @@ -449,4 +522,9 @@ protected String getEncodedTextDescription(int tagType) return null; } } + + protected static DecimalFormatSymbols getDecimalFormatSymbols(Locale locale) + { + return locale == null ? DecimalFormatSymbols.getInstance() : DecimalFormatSymbols.getInstance(locale); + } } diff --git a/Source/com/drew/metadata/adobe/AdobeJpegDescriptor.java b/Source/com/drew/metadata/adobe/AdobeJpegDescriptor.java index 3427f82e9..f6d6e790d 100644 --- a/Source/com/drew/metadata/adobe/AdobeJpegDescriptor.java +++ b/Source/com/drew/metadata/adobe/AdobeJpegDescriptor.java @@ -22,6 +22,7 @@ package com.drew.metadata.adobe; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import static com.drew.metadata.adobe.AdobeJpegDirectory.*; @@ -32,9 +33,9 @@ @SuppressWarnings("WeakerAccess") public class AdobeJpegDescriptor extends TagDescriptor { - public AdobeJpegDescriptor(AdobeJpegDirectory directory) + public AdobeJpegDescriptor(AdobeJpegDirectory directory, MetadataContext context) { - super(directory); + super(directory, context); } @Override diff --git a/Source/com/drew/metadata/adobe/AdobeJpegDirectory.java b/Source/com/drew/metadata/adobe/AdobeJpegDirectory.java index 6f44c0ede..34682b43f 100644 --- a/Source/com/drew/metadata/adobe/AdobeJpegDirectory.java +++ b/Source/com/drew/metadata/adobe/AdobeJpegDirectory.java @@ -23,6 +23,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.util.HashMap; @@ -57,8 +58,8 @@ public class AdobeJpegDirectory extends Directory { _tagNameMap.put(TAG_COLOR_TRANSFORM, "Color Transform"); } - public AdobeJpegDirectory() { - this.setDescriptor(new AdobeJpegDescriptor(this)); + public AdobeJpegDirectory(@NotNull MetadataContext context) { + this.setDescriptor(new AdobeJpegDescriptor(this, context)); } @NotNull diff --git a/Source/com/drew/metadata/adobe/AdobeJpegReader.java b/Source/com/drew/metadata/adobe/AdobeJpegReader.java index 78ca68280..b8ee2ef65 100644 --- a/Source/com/drew/metadata/adobe/AdobeJpegReader.java +++ b/Source/com/drew/metadata/adobe/AdobeJpegReader.java @@ -28,6 +28,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import java.io.IOException; import java.util.Collections; @@ -49,17 +50,17 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPE); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] bytes : segments) { if (bytes.length == 12 && PREAMBLE.equalsIgnoreCase(new String(bytes, 0, PREAMBLE.length()))) - extract(new SequentialByteArrayReader(bytes), metadata); + extract(new SequentialByteArrayReader(bytes), metadata, context); } } - public void extract(@NotNull SequentialReader reader, @NotNull Metadata metadata) + public void extract(@NotNull SequentialReader reader, @NotNull Metadata metadata, @NotNull MetadataContext context) { - Directory directory = new AdobeJpegDirectory(); + Directory directory = new AdobeJpegDirectory(context); metadata.addDirectory(directory); try { diff --git a/Source/com/drew/metadata/exif/ExifDescriptorBase.java b/Source/com/drew/metadata/exif/ExifDescriptorBase.java index 2c3a2fc71..b88ac63d3 100644 --- a/Source/com/drew/metadata/exif/ExifDescriptorBase.java +++ b/Source/com/drew/metadata/exif/ExifDescriptorBase.java @@ -27,11 +27,14 @@ import com.drew.lang.annotations.Nullable; import com.drew.lang.ByteArrayReader; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; import static com.drew.metadata.exif.ExifDirectoryBase.*; @@ -55,11 +58,17 @@ public abstract class ExifDescriptorBase extends TagDescrip // Ev=BV+Sv Sv=log2(ISOSpeedRating/3.125) // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32. + // TODO can be removed once context has been added to all sub-classes public ExifDescriptorBase(@NotNull T directory) { super(directory); } + public ExifDescriptorBase(@NotNull T directory, @NotNull MetadataContext context) + { + super(directory, context); + } + @Nullable @Override public String getDescription(int tagType) @@ -422,9 +431,11 @@ public String getXResolutionDescription() if (value == null) return null; final String unit = getResolutionDescription(); - return String.format("%s dots per %s", + final Locale locale = getContext().locale(); + return String.format(locale, + "%s dots per %s", value.toSimpleString(_allowDecimalRepresentationOfRationals), - unit == null ? "unit" : unit.toLowerCase()); + unit == null ? "unit" : unit.toLowerCase(locale)); } @Nullable @@ -434,9 +445,11 @@ public String getYResolutionDescription() if (value==null) return null; final String unit = getResolutionDescription(); - return String.format("%s dots per %s", + final Locale locale = getContext().locale(); + return String.format(locale, + "%s dots per %s", value.toSimpleString(_allowDecimalRepresentationOfRationals), - unit == null ? "unit" : unit.toLowerCase()); + unit == null ? "unit" : unit.toLowerCase(locale)); } @Nullable @@ -524,7 +537,7 @@ public String getReferenceBlackWhiteDescription() int whiteG = ints[3]; int blackB = ints[4]; int whiteB = ints[5]; - return String.format("[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB); + return String.format(getContext().locale(), "[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB); } /** @@ -545,7 +558,7 @@ public String getCfaPattern2Description() int[] repeatPattern = _directory.getIntArray(TAG_CFA_REPEAT_PATTERN_DIM); if (repeatPattern == null) - return String.format("Repeat Pattern not found for CFAPattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); + return String.format(getContext().locale(), "Repeat Pattern not found for CFAPattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); if (repeatPattern.length == 2 && values.length == (repeatPattern[0] * repeatPattern[1])) { @@ -559,7 +572,7 @@ public String getCfaPattern2Description() return formatCFAPattern(intpattern); } - return String.format("Unknown Pattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); + return String.format(getContext().locale(), "Unknown Pattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); } @Nullable @@ -610,7 +623,7 @@ public String getFNumberDescription() Rational value = _directory.getRational(TAG_FNUMBER); if (value == null) return null; - return getFStopDescription(value.doubleValue()); + return getFStopDescription(value.doubleValue(), getContext().locale()); } @Nullable @@ -694,7 +707,7 @@ public String getCompressedAverageBitsPerPixelDescription() @Nullable public String getShutterSpeedDescription() { - return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED); + return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED, getContext().locale()); } @Nullable @@ -704,7 +717,7 @@ public String getApertureValueDescription() if (aperture == null) return null; double fStop = PhotographicConversions.apertureToFStop(aperture); - return getFStopDescription(fStop); + return getFStopDescription(fStop, getContext().locale()); } @Nullable @@ -715,7 +728,7 @@ public String getBrightnessValueDescription() return null; if (value.getNumerator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0##"); + DecimalFormat formatter = new DecimalFormat("0.0##", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()); } @@ -735,7 +748,7 @@ public String getMaxApertureValueDescription() if (aperture == null) return null; double fStop = PhotographicConversions.apertureToFStop(aperture); - return getFStopDescription(fStop); + return getFStopDescription(fStop, getContext().locale()); } @Nullable @@ -748,7 +761,7 @@ public String getSubjectDistanceDescription() return "Infinity"; if (value.getNumerator() == 0) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0##"); + DecimalFormat formatter = new DecimalFormat("0.0##", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " metres"; } @@ -863,7 +876,7 @@ public String getFlashDescription() public String getFocalLengthDescription() { Rational value = _directory.getRational(TAG_FOCAL_LENGTH); - return value == null ? null : getFocalLengthDescription(value.doubleValue()); + return value == null ? null : getFocalLengthDescription(value.doubleValue(), getContext().locale()); } @Nullable @@ -880,7 +893,7 @@ public String getTemperatureDescription() return null; if (value.getDenominator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0"); + DecimalFormat formatter = new DecimalFormat("0.0", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " °C"; } @@ -892,7 +905,7 @@ public String getHumidityDescription() return null; if (value.getDenominator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0"); + DecimalFormat formatter = new DecimalFormat("0.0", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " %"; } @@ -904,7 +917,7 @@ public String getPressureDescription() return null; if (value.getDenominator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0"); + DecimalFormat formatter = new DecimalFormat("0.0", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " hPa"; } @@ -916,7 +929,7 @@ public String getWaterDepthDescription() return null; if (value.getDenominator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0##"); + DecimalFormat formatter = new DecimalFormat("0.0##", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " metres"; } @@ -928,7 +941,7 @@ public String getAccelerationDescription() return null; if (value.getDenominator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.0##"); + DecimalFormat formatter = new DecimalFormat("0.0##", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " mGal"; } @@ -940,7 +953,7 @@ public String getCameraElevationAngleDescription() return null; if (value.getDenominator() == 0xFFFFFFFFL) return "Unknown"; - DecimalFormat formatter = new DecimalFormat("0.##"); + DecimalFormat formatter = new DecimalFormat("0.##", getDecimalFormatSymbols()); return formatter.format(value.doubleValue()) + " degrees"; } @@ -1030,7 +1043,7 @@ public String getFocalPlaneXResolutionDescription() return null; final String unit = getFocalPlaneResolutionUnitDescription(); return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) - + (unit == null ? "" : " " + unit.toLowerCase()); + + (unit == null ? "" : " " + unit.toLowerCase(getContext().locale())); } @Nullable @@ -1041,7 +1054,7 @@ public String getFocalPlaneYResolutionDescription() return null; final String unit = getFocalPlaneResolutionUnitDescription(); return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) - + (unit == null ? "" : " " + unit.toLowerCase()); + + (unit == null ? "" : " " + unit.toLowerCase(getContext().locale())); } @Nullable @@ -1215,7 +1228,7 @@ public String getDigitalZoomRatioDescription() ? null : value.getNumerator() == 0 ? "Digital zoom not used" - : new DecimalFormat("0.#").format(value.doubleValue()); + : new DecimalFormat("0.#", getDecimalFormatSymbols()).format(value.doubleValue()); } @Nullable @@ -1226,7 +1239,7 @@ public String get35mmFilmEquivFocalLengthDescription() ? null : value == 0 ? "Unknown" - : getFocalLengthDescription(value); + : getFocalLengthDescription(value, getContext().locale()); } @Nullable @@ -1296,6 +1309,11 @@ public String getSubjectDistanceRangeDescription() @Nullable public String getLensSpecificationDescription() { - return getLensSpecificationDescription(TAG_LENS_SPECIFICATION); + return getLensSpecificationDescription(TAG_LENS_SPECIFICATION, getContext().locale()); + } + + private DecimalFormatSymbols getDecimalFormatSymbols() + { + return DecimalFormatSymbols.getInstance(getContext().locale()); } } diff --git a/Source/com/drew/metadata/exif/ExifReader.java b/Source/com/drew/metadata/exif/ExifReader.java index ff2e9a954..9905368e0 100644 --- a/Source/com/drew/metadata/exif/ExifReader.java +++ b/Source/com/drew/metadata/exif/ExifReader.java @@ -30,6 +30,7 @@ import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import java.io.IOException; import java.util.Collections; @@ -53,14 +54,14 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP1); } - public void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType) + public void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType, @NotNull MetadataContext context) { assert(segmentType == JpegSegmentType.APP1); for (byte[] segmentBytes : segments) { // Segment must have the expected preamble if (startsWithJpegExifPreamble(segmentBytes)) { - extract(new ByteArrayReader(segmentBytes), metadata, JPEG_SEGMENT_PREAMBLE.length()); + extract(new ByteArrayReader(segmentBytes), metadata, JPEG_SEGMENT_PREAMBLE.length(), null, context); } } } @@ -78,7 +79,7 @@ public void extract(@NotNull final RandomAccessReader reader, @NotNull final Met extract(reader, metadata, 0); } - /** Reads TIFF formatted Exif data a specified offset within a {@link RandomAccessReader}. */ + /** Reads TIFF formatted Exif data at a specified offset within a {@link RandomAccessReader}. */ public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset) { extract(reader, metadata, readerOffset, null); @@ -87,7 +88,14 @@ public void extract(@NotNull final RandomAccessReader reader, @NotNull final Met /** Reads TIFF formatted Exif data at a specified offset within a {@link RandomAccessReader}. */ public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset, @Nullable Directory parentDirectory) { - ExifTiffHandler exifTiffHandler = new ExifTiffHandler(metadata, parentDirectory); + // TODO document this default context? + extract(reader, metadata, readerOffset, parentDirectory, new MetadataContext()); + } + + /** Reads TIFF formatted Exif data at a specified offset within a {@link RandomAccessReader}. */ + public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset, @Nullable Directory parentDirectory, @NotNull MetadataContext context) + { + ExifTiffHandler exifTiffHandler = new ExifTiffHandler(metadata, parentDirectory, context); try { // Read the TIFF-formatted Exif data diff --git a/Source/com/drew/metadata/exif/ExifSubIFDDescriptor.java b/Source/com/drew/metadata/exif/ExifSubIFDDescriptor.java index 6943a9a29..a6072fddf 100644 --- a/Source/com/drew/metadata/exif/ExifSubIFDDescriptor.java +++ b/Source/com/drew/metadata/exif/ExifSubIFDDescriptor.java @@ -21,6 +21,10 @@ package com.drew.metadata.exif; import com.drew.lang.annotations.NotNull; +import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; + +import java.util.Locale; /** * Provides human-readable string representations of tag values stored in a {@link ExifSubIFDDirectory}. @@ -30,8 +34,8 @@ @SuppressWarnings("WeakerAccess") public class ExifSubIFDDescriptor extends ExifDescriptorBase { - public ExifSubIFDDescriptor(@NotNull ExifSubIFDDirectory directory) + public ExifSubIFDDescriptor(@NotNull ExifSubIFDDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } } diff --git a/Source/com/drew/metadata/exif/ExifSubIFDDirectory.java b/Source/com/drew/metadata/exif/ExifSubIFDDirectory.java index dee891270..0cfe28c3e 100644 --- a/Source/com/drew/metadata/exif/ExifSubIFDDirectory.java +++ b/Source/com/drew/metadata/exif/ExifSubIFDDirectory.java @@ -23,9 +23,11 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.util.Date; import java.util.HashMap; +import java.util.Locale; import java.util.TimeZone; /** @@ -39,9 +41,9 @@ public class ExifSubIFDDirectory extends ExifDirectoryBase /** This tag is a pointer to the Exif Interop IFD. */ public static final int TAG_INTEROP_OFFSET = 0xA005; - public ExifSubIFDDirectory() + public ExifSubIFDDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new ExifSubIFDDescriptor(this)); + this.setDescriptor(new ExifSubIFDDescriptor(this, context)); } @NotNull diff --git a/Source/com/drew/metadata/exif/ExifTiffHandler.java b/Source/com/drew/metadata/exif/ExifTiffHandler.java index 7e4dc5784..3aeba9dca 100644 --- a/Source/com/drew/metadata/exif/ExifTiffHandler.java +++ b/Source/com/drew/metadata/exif/ExifTiffHandler.java @@ -38,6 +38,7 @@ import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.StringValue; import com.drew.metadata.apple.AppleRunTimeReader; import com.drew.metadata.exif.makernotes.*; @@ -59,7 +60,13 @@ public class ExifTiffHandler extends DirectoryTiffHandler { public ExifTiffHandler(@NotNull Metadata metadata, @Nullable Directory parentDirectory) { - super(metadata, parentDirectory); + // TODO document this default context? + super(metadata, parentDirectory, new MetadataContext()); + } + + public ExifTiffHandler(@NotNull Metadata metadata, @Nullable Directory parentDirectory, @NotNull MetadataContext context) + { + super(metadata, parentDirectory, context); } public void setTiffMarker(int marker) throws TiffProcessingException @@ -79,7 +86,7 @@ public void setTiffMarker(int marker) throws TiffProcessingException pushDirectory(PanasonicRawIFD0Directory.class); break; default: - throw new TiffProcessingException(String.format("Unexpected TIFF marker: 0x%X", marker)); + throw new TiffProcessingException(String.format(_context.locale(), "Unexpected TIFF marker: 0x%X", marker)); } } diff --git a/Source/com/drew/metadata/exif/GpsDescriptor.java b/Source/com/drew/metadata/exif/GpsDescriptor.java index 72062b206..c6e19183e 100644 --- a/Source/com/drew/metadata/exif/GpsDescriptor.java +++ b/Source/com/drew/metadata/exif/GpsDescriptor.java @@ -24,9 +24,11 @@ import com.drew.lang.Rational; import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import static com.drew.metadata.exif.GpsDirectory.*; @@ -38,9 +40,10 @@ @SuppressWarnings("WeakerAccess") public class GpsDescriptor extends TagDescriptor { - public GpsDescriptor(@NotNull GpsDirectory directory) + + public GpsDescriptor(@NotNull GpsDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override @@ -111,14 +114,14 @@ private String getGpsVersionIdDescription() public String getGpsLatitudeDescription() { GeoLocation location = _directory.getGeoLocation(); - return location == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude()); + return location == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude(), _context.locale()); } @Nullable public String getGpsLongitudeDescription() { GeoLocation location = _directory.getGeoLocation(); - return location == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude()); + return location == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude(), _context.locale()); } @Nullable @@ -126,10 +129,11 @@ public String getGpsTimeStampDescription() { // time in hour, min, sec Rational[] timeComponents = _directory.getRationalArray(TAG_TIME_STAMP); - DecimalFormat df = new DecimalFormat("00.000"); + DecimalFormat df = new DecimalFormat("00.000", getDecimalFormatSymbols(_context.locale())); return timeComponents == null ? null - : String.format("%02d:%02d:%s UTC", + : String.format(_context.locale(), + "%02d:%02d:%s UTC", timeComponents[0].intValue(), timeComponents[1].intValue(), df.format(timeComponents[2].doubleValue())); @@ -159,7 +163,7 @@ private String getGeoLocationDimension(int tagValue, int tagRef, String positive Double dec = GeoLocation.degreesMinutesSecondsToDecimal( values[0], values[1], values[2], ref.equalsIgnoreCase(positiveRef)); - return dec == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(dec); + return dec == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(dec, _context.locale()); } @Nullable @@ -187,9 +191,10 @@ public String getGpsDestDistanceDescription() if (value == null) return null; final String unit = getGpsDestinationReferenceDescription(); - return String.format("%s %s", - new DecimalFormat("0.##").format(value.doubleValue()), - unit == null ? "unit" : unit.toLowerCase()); + return String.format(_context.locale(), + "%s %s", + new DecimalFormat("0.##", getDecimalFormatSymbols(_context.locale())).format(value.doubleValue()), + unit == null ? "unit" : unit.toLowerCase(_context.locale())); } @Nullable @@ -198,7 +203,7 @@ public String getGpsDirectionDescription(int tagType) Rational angle = _directory.getRational(tagType); // provide a decimal version of rational numbers in the description, to avoid strings like "35334/199 degrees" String value = angle != null - ? new DecimalFormat("0.##").format(angle.doubleValue()) + ? new DecimalFormat("0.##", getDecimalFormatSymbols(_context.locale())).format(angle.doubleValue()) : _directory.getString(tagType); return value == null || value.trim().length() == 0 ? null : value.trim() + " degrees"; } @@ -223,7 +228,8 @@ public String getGpsDirectionReferenceDescription(int tagType) public String getGpsDopDescription() { final Rational value = _directory.getRational(TAG_DOP); - return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()); + final DecimalFormatSymbols symbols = getDecimalFormatSymbols(_context.locale()); + return value == null ? null : new DecimalFormat("0.##", symbols).format(value.doubleValue()); } @Nullable @@ -252,8 +258,8 @@ public String getGpsSpeedDescription() return null; final String unit = getGpsSpeedRefDescription(); return String.format("%s %s", - new DecimalFormat("0.##").format(value.doubleValue()), - unit == null ? "unit" : unit.toLowerCase()); + new DecimalFormat("0.##", getDecimalFormatSymbols(_context.locale())).format(value.doubleValue()), + unit == null ? "unit" : unit.toLowerCase(_context.locale())); } @Nullable @@ -298,7 +304,8 @@ public String getGpsAltitudeRefDescription() public String getGpsAltitudeDescription() { final Rational value = _directory.getRational(TAG_ALTITUDE); - return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()) + " metres"; + final DecimalFormatSymbols symbols = getDecimalFormatSymbols(_context.locale()); + return value == null ? null : new DecimalFormat("0.##", symbols).format(value.doubleValue()) + " metres"; } @Nullable @@ -323,7 +330,8 @@ public String getGpsDifferentialDescription() public String getGpsHPositioningErrorDescription() { final Rational value = _directory.getRational(TAG_H_POSITIONING_ERROR); - return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()) + " metres"; + final DecimalFormatSymbols symbols = getDecimalFormatSymbols(_context.locale()); + return value == null ? null : new DecimalFormat("0.##", symbols).format(value.doubleValue()) + " metres"; } @Nullable diff --git a/Source/com/drew/metadata/exif/GpsDirectory.java b/Source/com/drew/metadata/exif/GpsDirectory.java index 9890ab4dc..f61dbcd14 100644 --- a/Source/com/drew/metadata/exif/GpsDirectory.java +++ b/Source/com/drew/metadata/exif/GpsDirectory.java @@ -24,6 +24,7 @@ import com.drew.lang.Rational; import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import java.text.DateFormat; import java.text.ParseException; @@ -146,9 +147,9 @@ public class GpsDirectory extends ExifDirectoryBase _tagNameMap.put(TAG_H_POSITIONING_ERROR, "GPS Horizontal Positioning Error"); } - public GpsDirectory() + public GpsDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new GpsDescriptor(this)); + this.setDescriptor(new GpsDescriptor(this, context)); } @Override @@ -194,7 +195,7 @@ public GeoLocation getGeoLocation() if (lat == null || lon == null) return null; - return new GeoLocation(lat, lon); + return new GeoLocation(lat, lon, getLocale()); } /** @@ -218,7 +219,7 @@ public Date getGpsDate() String dateTime = String.format(Locale.US, "%s %02d:%02d:%02.3f UTC", date, timeComponents[0].intValue(), timeComponents[1].intValue(), timeComponents[2].doubleValue()); try { - DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss.S z"); + DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss.S z", getLocale()); return parser.parse(dateTime); } catch (ParseException e) { return null; diff --git a/Source/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java b/Source/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java index 665e81129..b56be7f60 100644 --- a/Source/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java +++ b/Source/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java @@ -521,7 +521,7 @@ public String getMaxApertureDescription() return null; if (value > 512) return String.format("Unknown (%d)", value); - return getFStopDescription(Math.exp(decodeCanonEv(value) * Math.log(2.0) / 2.0)); + return getFStopDescription(Math.exp(decodeCanonEv(value) * Math.log(2.0) / 2.0), getContext().locale()); } @Nullable @@ -532,7 +532,7 @@ public String getMinApertureDescription() return null; if (value > 512) return String.format("Unknown (%d)", value); - return getFStopDescription(Math.exp(decodeCanonEv(value) * Math.log(2.0) / 2.0)); + return getFStopDescription(Math.exp(decodeCanonEv(value) * Math.log(2.0) / 2.0), getContext().locale()); } @Nullable @@ -849,7 +849,7 @@ public String getDisplayApertureDescription() if (value == 0xFFFF) return value.toString(); - return getFStopDescription(value / 10f); + return getFStopDescription(value / 10f, getContext().locale()); } @Nullable diff --git a/Source/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java b/Source/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java index e4fc056d0..7b46451dc 100644 --- a/Source/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java +++ b/Source/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java @@ -155,7 +155,7 @@ public String getWhiteBalanceDescription() public String getObjectDistanceDescription() { Integer value = _directory.getInteger(TAG_OBJECT_DISTANCE); - return value == null ? null : getFocalLengthDescription(value); + return value == null ? null : getFocalLengthDescription(value, getContext().locale()); } @Nullable diff --git a/Source/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java b/Source/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java index 853edbddf..98809876c 100644 --- a/Source/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java +++ b/Source/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java @@ -231,7 +231,7 @@ public String getSaturationDescription() public String getFocalLengthDescription() { Double value = _directory.getDoubleObject(TAG_FOCAL_LENGTH); - return value == null ? null : getFocalLengthDescription(value / 10d); + return value == null ? null : getFocalLengthDescription(value / 10d, getContext().locale()); } @Nullable diff --git a/Source/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java b/Source/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java index 2df3f28ef..e86e8016c 100644 --- a/Source/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java +++ b/Source/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java @@ -338,7 +338,7 @@ public String getIsoSettingDescription() @Nullable public String getLensDescription() { - return getLensSpecificationDescription(TAG_LENS); + return getLensSpecificationDescription(TAG_LENS, getContext().locale()); } @Nullable diff --git a/Source/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDescriptor.java b/Source/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDescriptor.java index 3c9347888..1a4f582f7 100644 --- a/Source/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDescriptor.java +++ b/Source/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDescriptor.java @@ -231,7 +231,7 @@ public String getMeteringModeDescription() @Nullable public String getExposureShiftDescription() { - return getRationalOrDoubleString(TagExposureShift); + return getRationalOrDoubleString(TagExposureShift, getContext().locale()); } @Nullable diff --git a/Source/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java b/Source/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java index 112bcd105..0374576ee 100644 --- a/Source/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java +++ b/Source/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java @@ -321,7 +321,7 @@ public String getApexApertureDescription() return null; double fStop = Math.pow((value/16d) - 0.5, 2); - return getFStopDescription(fStop); + return getFStopDescription(fStop, getContext().locale()); } @Nullable @@ -374,7 +374,7 @@ public String getIntervalNumberDescription() public String getFocalLengthDescription() { Long value = _directory.getLongObject(CameraSettings.TAG_FOCAL_LENGTH); - return value == null ? null : getFocalLengthDescription(value/256d); + return value == null ? null : getFocalLengthDescription(value/256d, getContext().locale()); } @Nullable @@ -442,7 +442,7 @@ public String getMaxApertureAtFocalLengthDescription() if (value == null) return null; double fStop = Math.pow((value/16d) - 0.5, 2); - return getFStopDescription(fStop); + return getFStopDescription(fStop, getContext().locale()); } @Nullable @@ -828,7 +828,7 @@ public String getOneTouchWbDescription() @Nullable public String getShutterSpeedDescription() { - return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED_VALUE); + return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED_VALUE, getContext().locale()); } @Nullable @@ -848,7 +848,7 @@ public String getApertureValueDescription() if (aperture == null) return null; double fStop = PhotographicConversions.apertureToFStop(aperture); - return getFStopDescription(fStop); + return getFStopDescription(fStop, getContext().locale()); } @Nullable diff --git a/Source/com/drew/metadata/icc/IccDescriptor.java b/Source/com/drew/metadata/icc/IccDescriptor.java index 98b11393c..26fb33c60 100644 --- a/Source/com/drew/metadata/icc/IccDescriptor.java +++ b/Source/com/drew/metadata/icc/IccDescriptor.java @@ -25,6 +25,7 @@ import com.drew.lang.RandomAccessReader; import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import java.io.IOException; @@ -40,9 +41,9 @@ @SuppressWarnings("WeakerAccess") public class IccDescriptor extends TagDescriptor { - public IccDescriptor(@NotNull IccDirectory directory) + public IccDescriptor(@NotNull IccDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override @@ -114,7 +115,7 @@ private String getTagDataString(int tagType) observerString = "1964 10\u00B0"; break; default: - observerString = String.format("Unknown %d", observerType); + observerString = String.format(getContext().locale(), "Unknown %d", observerType); } String geometryString; switch (geometryType) { @@ -128,7 +129,7 @@ private String getTagDataString(int tagType) geometryString = "0/d or d/0"; break; default: - geometryString = String.format("Unknown %d", observerType); + geometryString = String.format(getContext().locale(), "Unknown %d", observerType); } String illuminantString; switch (illuminantType) { @@ -160,16 +161,16 @@ private String getTagDataString(int tagType) illuminantString = "F8"; break; default: - illuminantString = String.format("Unknown %d", illuminantType); + illuminantString = String.format(getContext().locale(), "Unknown %d", illuminantType); break; } - DecimalFormat format = new DecimalFormat("0.###"); - return String.format("%s Observer, Backing (%s, %s, %s), Geometry %s, Flare %d%%, Illuminant %s", + DecimalFormat format = new DecimalFormat("0.###", getDecimalFormatSymbols(getContext().locale())); + return String.format(getContext().locale(), "%s Observer, Backing (%s, %s, %s), Geometry %s, Flare %d%%, Illuminant %s", observerString, format.format(x), format.format(y), format.format(z), geometryString, Math.round(flare * 100), illuminantString); } case ICC_TAG_TYPE_XYZ_ARRAY: { StringBuilder res = new StringBuilder(); - DecimalFormat format = new DecimalFormat("0.####"); + DecimalFormat format = new DecimalFormat("0.####", getDecimalFormatSymbols(getContext().locale())); int count = (bytes.length - 8) / 12; for (int i = 0; i < count; i++) { float x = reader.getS15Fixed16(8 + i * 12); @@ -214,7 +215,7 @@ private String getTagDataString(int tagType) return res.toString(); } default: - return String.format("%s (0x%08X): %d bytes", IccReader.getStringFromInt32(iccTagType), iccTagType, bytes.length); + return String.format(getContext().locale(), "%s (0x%08X): %d bytes", IccReader.getStringFromInt32(iccTagType), iccTagType, bytes.length); } } catch (IOException e) { // TODO decode these values during IccReader.extract so we can report any errors at that time @@ -281,7 +282,7 @@ private String getPlatformDescription() case 0x54474E54: return "Taligent, Inc."; default: - return String.format("Unknown (%s)", str); + return String.format(getContext().locale(), "Unknown (%s)", str); } } @@ -315,7 +316,7 @@ private String getProfileClassDescription() case 0x6E6D636C: return "Named Color"; default: - return String.format("Unknown (%s)", str); + return String.format(getContext().locale(), "Unknown (%s)", str); } } @@ -331,7 +332,7 @@ private String getProfileVersionDescription() int r = (value & 0x00F00000) >> 20; int R = (value & 0x000F0000) >> 16; - return String.format("%d.%d.%d", m, r, R); + return String.format(getContext().locale(), "%d.%d.%d", m, r, R); } private static int getInt32FromString(@NotNull String string) throws IOException diff --git a/Source/com/drew/metadata/icc/IccDirectory.java b/Source/com/drew/metadata/icc/IccDirectory.java index 4b25f5795..20ad369ac 100644 --- a/Source/com/drew/metadata/icc/IccDirectory.java +++ b/Source/com/drew/metadata/icc/IccDirectory.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.util.HashMap; @@ -187,9 +188,9 @@ public class IccDirectory extends Directory _tagNameMap.put(TAG_APPLE_MULTI_LANGUAGE_PROFILE_NAME, "Apple Multi-language Profile Name"); } - public IccDirectory() + public IccDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new IccDescriptor(this)); + this.setDescriptor(new IccDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/icc/IccReader.java b/Source/com/drew/metadata/icc/IccReader.java index 46b59bf10..68734006d 100644 --- a/Source/com/drew/metadata/icc/IccReader.java +++ b/Source/com/drew/metadata/icc/IccReader.java @@ -29,10 +29,12 @@ import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataReader; import java.io.IOException; import java.util.Collections; +import java.util.Locale; /** * Reads an ICC profile. @@ -57,7 +59,7 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP2); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { final int preambleLength = JPEG_SEGMENT_PREAMBLE.length(); @@ -86,7 +88,7 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada } if (buffer != null) - extract(new ByteArrayReader(buffer), metadata); + extract(new ByteArrayReader(buffer), metadata, null, context); } public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata) @@ -95,10 +97,16 @@ public void extract(@NotNull final RandomAccessReader reader, @NotNull final Met } public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, @Nullable Directory parentDirectory) + { + // TODO document this default context? + extract(reader, metadata, parentDirectory, new MetadataContext()); + } + + public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, @Nullable Directory parentDirectory, @NotNull final MetadataContext context) { // TODO review whether the 'tagPtr' values below really do require RandomAccessReader or whether SequentialReader may be used instead - IccDirectory directory = new IccDirectory(); + IccDirectory directory = new IccDirectory(context); if (parentDirectory != null) directory.setParent(parentDirectory); @@ -113,7 +121,7 @@ public void extract(@NotNull final RandomAccessReader reader, @NotNull final Met set4ByteString(directory, IccDirectory.TAG_PROFILE_CLASS, reader); set4ByteString(directory, IccDirectory.TAG_COLOR_SPACE, reader); set4ByteString(directory, IccDirectory.TAG_PROFILE_CONNECTION_SPACE, reader); - setDate(directory, IccDirectory.TAG_PROFILE_DATETIME, reader); + setDate(directory, IccDirectory.TAG_PROFILE_DATETIME, reader, context.locale()); set4ByteString(directory, IccDirectory.TAG_SIGNATURE, reader); set4ByteString(directory, IccDirectory.TAG_PLATFORM, reader); setInt32(directory, IccDirectory.TAG_CMM_FLAGS, reader); @@ -180,7 +188,7 @@ private void setInt64(@NotNull Directory directory, int tagType, @NotNull Random } @SuppressWarnings({"SameParameterValue", "MagicConstant"}) - private void setDate(@NotNull final IccDirectory directory, final int tagType, @NotNull RandomAccessReader reader) throws IOException + private void setDate(@NotNull final IccDirectory directory, final int tagType, @NotNull RandomAccessReader reader, @NotNull Locale locale) throws IOException { final int y = reader.getUInt16(tagType); final int m = reader.getUInt16(tagType + 2); @@ -191,12 +199,13 @@ private void setDate(@NotNull final IccDirectory directory, final int tagType, @ if (DateUtil.isValidDate(y, m - 1, d) && DateUtil.isValidTime(h, M, s)) { - String dateString = String.format("%04d:%02d:%02d %02d:%02d:%02d", y, m, d, h, M, s); + String dateString = String.format(locale, "%04d:%02d:%02d %02d:%02d:%02d", y, m, d, h, M, s); directory.setString(tagType, dateString); } else { directory.addError(String.format( + locale, "ICC data describes an invalid date/time: year=%d month=%d day=%d hour=%d minute=%d second=%d", y, m, d, h, M, s)); } diff --git a/Source/com/drew/metadata/iptc/IptcDescriptor.java b/Source/com/drew/metadata/iptc/IptcDescriptor.java index 139d15b2a..b57b0e17e 100644 --- a/Source/com/drew/metadata/iptc/IptcDescriptor.java +++ b/Source/com/drew/metadata/iptc/IptcDescriptor.java @@ -23,6 +23,7 @@ import com.drew.lang.StringUtil; import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import static com.drew.metadata.iptc.IptcDirectory.*; @@ -37,9 +38,9 @@ @SuppressWarnings("WeakerAccess") public class IptcDescriptor extends TagDescriptor { - public IptcDescriptor(@NotNull IptcDirectory directory) + public IptcDescriptor(@NotNull IptcDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override diff --git a/Source/com/drew/metadata/iptc/IptcDirectory.java b/Source/com/drew/metadata/iptc/IptcDirectory.java index 626e6070a..1816339b7 100644 --- a/Source/com/drew/metadata/iptc/IptcDirectory.java +++ b/Source/com/drew/metadata/iptc/IptcDirectory.java @@ -23,6 +23,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.text.DateFormat; import java.text.ParseException; @@ -210,9 +211,9 @@ public class IptcDirectory extends Directory _tagNameMap.put(TAG_OBJECT_PREVIEW_DATA, "Object Data Preview Data"); } - public IptcDirectory() + public IptcDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new IptcDescriptor(this)); + this.setDescriptor(new IptcDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/iptc/IptcReader.java b/Source/com/drew/metadata/iptc/IptcReader.java index c1956c4fb..d31366ca2 100644 --- a/Source/com/drew/metadata/iptc/IptcReader.java +++ b/Source/com/drew/metadata/iptc/IptcReader.java @@ -28,6 +28,7 @@ import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.StringValue; import java.io.IOException; @@ -65,12 +66,12 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPD); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { // Ensure data starts with the IPTC marker byte if (segmentBytes.length != 0 && segmentBytes[0] == IptcMarkerByte) { - extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.length); + extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.length, context); } } } @@ -80,7 +81,8 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada */ public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length) { - extract(reader, metadata, length, null); + // TODO document this default context? + extract(reader, metadata, length, null, new MetadataContext()); } /** @@ -88,7 +90,24 @@ public void extract(@NotNull final SequentialReader reader, @NotNull final Metad */ public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length, @Nullable Directory parentDirectory) { - IptcDirectory directory = new IptcDirectory(); + // TODO document this default context? + extract(reader, metadata, length, parentDirectory, new MetadataContext()); + } + + /** + * Performs the IPTC data extraction, adding found values to the specified instance of {@link Metadata}. + */ + public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length, @NotNull MetadataContext context) + { + extract(reader, metadata, length, null, context); + } + + /** + * Performs the IPTC data extraction, adding found values to the specified instance of {@link Metadata}. + */ + public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length, @Nullable Directory parentDirectory, @NotNull MetadataContext context) + { + IptcDirectory directory = new IptcDirectory(context); metadata.addDirectory(directory); if (parentDirectory != null) diff --git a/Source/com/drew/metadata/jfif/JfifDescriptor.java b/Source/com/drew/metadata/jfif/JfifDescriptor.java index 1ab9a2c3e..0d9e767ca 100644 --- a/Source/com/drew/metadata/jfif/JfifDescriptor.java +++ b/Source/com/drew/metadata/jfif/JfifDescriptor.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import static com.drew.metadata.jfif.JfifDirectory.*; @@ -39,9 +40,9 @@ @SuppressWarnings("WeakerAccess") public class JfifDescriptor extends TagDescriptor { - public JfifDescriptor(@NotNull JfifDirectory directory) + public JfifDescriptor(@NotNull JfifDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override @@ -68,7 +69,7 @@ public String getImageVersionDescription() Integer value = _directory.getInteger(TAG_VERSION); if (value==null) return null; - return String.format("%d.%d", (value & 0xFF00) >> 8, value & 0xFF); + return String.format(getContext().locale(), "%d.%d", (value & 0xFF00) >> 8, value & 0xFF); } @Nullable @@ -77,7 +78,8 @@ public String getImageResYDescription() Integer value = _directory.getInteger(TAG_RESY); if (value==null) return null; - return String.format("%d dot%s", + return String.format(getContext().locale(), + "%d dot%s", value, value==1 ? "" : "s"); } @@ -88,7 +90,8 @@ public String getImageResXDescription() Integer value = _directory.getInteger(TAG_RESX); if (value==null) return null; - return String.format("%d dot%s", + return String.format(getContext().locale(), + "%d dot%s", value, value==1 ? "" : "s"); } diff --git a/Source/com/drew/metadata/jfif/JfifDirectory.java b/Source/com/drew/metadata/jfif/JfifDirectory.java index b8ea3f87f..9f7f6ba83 100644 --- a/Source/com/drew/metadata/jfif/JfifDirectory.java +++ b/Source/com/drew/metadata/jfif/JfifDirectory.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataException; import java.util.HashMap; @@ -55,9 +56,9 @@ public class JfifDirectory extends Directory _tagNameMap.put(TAG_THUMB_HEIGHT, "Thumbnail Height Pixels"); } - public JfifDirectory() + public JfifDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new JfifDescriptor(this)); + this.setDescriptor(new JfifDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/jfif/JfifReader.java b/Source/com/drew/metadata/jfif/JfifReader.java index 5923ee20b..32532b1fc 100644 --- a/Source/com/drew/metadata/jfif/JfifReader.java +++ b/Source/com/drew/metadata/jfif/JfifReader.java @@ -26,6 +26,7 @@ import com.drew.lang.RandomAccessReader; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataReader; import java.io.IOException; @@ -51,22 +52,27 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP0); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { // Skip segments not starting with the required header if (segmentBytes.length >= PREAMBLE.length() && PREAMBLE.equals(new String(segmentBytes, 0, PREAMBLE.length()))) - extract(new ByteArrayReader(segmentBytes), metadata); + extract(new ByteArrayReader(segmentBytes), metadata, context); } } + public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata) { + // TODO document this default context? + extract(reader, metadata, new MetadataContext()); + } + /** * Performs the Jfif data extraction, adding found values to the specified * instance of {@link Metadata}. */ - public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata) + public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, @NotNull final MetadataContext context) { - JfifDirectory directory = new JfifDirectory(); + JfifDirectory directory = new JfifDirectory(context); metadata.addDirectory(directory); try { diff --git a/Source/com/drew/metadata/jfxx/JfxxDescriptor.java b/Source/com/drew/metadata/jfxx/JfxxDescriptor.java index 3623736cb..cf05b665f 100644 --- a/Source/com/drew/metadata/jfxx/JfxxDescriptor.java +++ b/Source/com/drew/metadata/jfxx/JfxxDescriptor.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import static com.drew.metadata.jfxx.JfxxDirectory.*; @@ -39,9 +40,9 @@ @SuppressWarnings("WeakerAccess") public class JfxxDescriptor extends TagDescriptor { - public JfxxDescriptor(@NotNull JfxxDirectory directory) + public JfxxDescriptor(@NotNull JfxxDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override diff --git a/Source/com/drew/metadata/jfxx/JfxxDirectory.java b/Source/com/drew/metadata/jfxx/JfxxDirectory.java index 7acbe1c3f..05fc63b71 100644 --- a/Source/com/drew/metadata/jfxx/JfxxDirectory.java +++ b/Source/com/drew/metadata/jfxx/JfxxDirectory.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataException; import java.util.HashMap; @@ -44,9 +45,9 @@ public class JfxxDirectory extends Directory _tagNameMap.put(TAG_EXTENSION_CODE, "Extension Code"); } - public JfxxDirectory() + public JfxxDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new JfxxDescriptor(this)); + this.setDescriptor(new JfxxDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/jfxx/JfxxReader.java b/Source/com/drew/metadata/jfxx/JfxxReader.java index 63fbb9ab7..2c586e226 100644 --- a/Source/com/drew/metadata/jfxx/JfxxReader.java +++ b/Source/com/drew/metadata/jfxx/JfxxReader.java @@ -25,7 +25,9 @@ import com.drew.lang.ByteArrayReader; import com.drew.lang.RandomAccessReader; import com.drew.lang.annotations.NotNull; +import com.drew.lang.annotations.Nullable; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataReader; import java.io.IOException; @@ -51,22 +53,29 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP0); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { // Skip segments not starting with the required header if (segmentBytes.length >= PREAMBLE.length() && PREAMBLE.equals(new String(segmentBytes, 0, PREAMBLE.length()))) - extract(new ByteArrayReader(segmentBytes), metadata); + extract(new ByteArrayReader(segmentBytes), metadata, context); } } + @Override + public void extract(RandomAccessReader reader, Metadata metadata) + { + // TODO document this default context? + extract(reader, metadata, new MetadataContext()); + } + /** * Performs the JFXX data extraction, adding found values to the specified * instance of {@link Metadata}. */ - public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata) + public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, @NotNull final MetadataContext context) { - JfxxDirectory directory = new JfxxDirectory(); + JfxxDirectory directory = new JfxxDirectory(context); metadata.addDirectory(directory); try { diff --git a/Source/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java b/Source/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java index 4b1d14527..658722d91 100644 --- a/Source/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java +++ b/Source/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import static com.drew.metadata.jpeg.HuffmanTablesDirectory.*; @@ -39,9 +40,9 @@ @SuppressWarnings("WeakerAccess") public class HuffmanTablesDescriptor extends TagDescriptor { - public HuffmanTablesDescriptor(@NotNull HuffmanTablesDirectory directory) + public HuffmanTablesDescriptor(@NotNull HuffmanTablesDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override diff --git a/Source/com/drew/metadata/jpeg/HuffmanTablesDirectory.java b/Source/com/drew/metadata/jpeg/HuffmanTablesDirectory.java index 2b975f494..dd8ef9d83 100644 --- a/Source/com/drew/metadata/jpeg/HuffmanTablesDirectory.java +++ b/Source/com/drew/metadata/jpeg/HuffmanTablesDirectory.java @@ -27,6 +27,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataException; /** @@ -128,9 +129,9 @@ public class HuffmanTablesDirectory extends Directory _tagNameMap.put(TAG_NUMBER_OF_TABLES, "Number of Tables"); } - public HuffmanTablesDirectory() + public HuffmanTablesDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new HuffmanTablesDescriptor(this)); + this.setDescriptor(new HuffmanTablesDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/jpeg/JpegCommentDescriptor.java b/Source/com/drew/metadata/jpeg/JpegCommentDescriptor.java index e44014cf5..70b29e595 100644 --- a/Source/com/drew/metadata/jpeg/JpegCommentDescriptor.java +++ b/Source/com/drew/metadata/jpeg/JpegCommentDescriptor.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; /** @@ -32,9 +33,9 @@ @SuppressWarnings("WeakerAccess") public class JpegCommentDescriptor extends TagDescriptor { - public JpegCommentDescriptor(@NotNull JpegCommentDirectory directory) + public JpegCommentDescriptor(@NotNull JpegCommentDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Nullable diff --git a/Source/com/drew/metadata/jpeg/JpegCommentDirectory.java b/Source/com/drew/metadata/jpeg/JpegCommentDirectory.java index 2eb92f6b0..4d6213c25 100644 --- a/Source/com/drew/metadata/jpeg/JpegCommentDirectory.java +++ b/Source/com/drew/metadata/jpeg/JpegCommentDirectory.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.util.HashMap; @@ -46,9 +47,9 @@ public class JpegCommentDirectory extends Directory _tagNameMap.put(TAG_COMMENT, "JPEG Comment"); } - public JpegCommentDirectory() + public JpegCommentDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new JpegCommentDescriptor(this)); + this.setDescriptor(new JpegCommentDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/jpeg/JpegCommentReader.java b/Source/com/drew/metadata/jpeg/JpegCommentReader.java index f50bbf742..66ab7e632 100644 --- a/Source/com/drew/metadata/jpeg/JpegCommentReader.java +++ b/Source/com/drew/metadata/jpeg/JpegCommentReader.java @@ -24,6 +24,7 @@ import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.StringValue; import java.util.Collections; @@ -42,10 +43,10 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.COM); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { - JpegCommentDirectory directory = new JpegCommentDirectory(); + JpegCommentDirectory directory = new JpegCommentDirectory(context); metadata.addDirectory(directory); // The entire contents of the directory are the comment diff --git a/Source/com/drew/metadata/jpeg/JpegDescriptor.java b/Source/com/drew/metadata/jpeg/JpegDescriptor.java index 2f2d964c3..c5bd029e3 100644 --- a/Source/com/drew/metadata/jpeg/JpegDescriptor.java +++ b/Source/com/drew/metadata/jpeg/JpegDescriptor.java @@ -22,6 +22,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import static com.drew.metadata.jpeg.JpegDirectory.*; @@ -35,9 +36,9 @@ @SuppressWarnings("WeakerAccess") public class JpegDescriptor extends TagDescriptor { - public JpegDescriptor(@NotNull JpegDirectory directory) + public JpegDescriptor(@NotNull JpegDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override diff --git a/Source/com/drew/metadata/jpeg/JpegDhtReader.java b/Source/com/drew/metadata/jpeg/JpegDhtReader.java index cba2dda87..d8eeb96b6 100644 --- a/Source/com/drew/metadata/jpeg/JpegDhtReader.java +++ b/Source/com/drew/metadata/jpeg/JpegDhtReader.java @@ -25,7 +25,9 @@ import com.drew.lang.SequentialByteArrayReader; import com.drew.lang.SequentialReader; import com.drew.lang.annotations.NotNull; +import com.drew.lang.annotations.Nullable; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.jpeg.HuffmanTablesDirectory.HuffmanTable; import com.drew.metadata.jpeg.HuffmanTablesDirectory.HuffmanTable.HuffmanTableClass; import java.io.IOException; @@ -44,10 +46,10 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.DHT); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { - extract(new SequentialByteArrayReader(segmentBytes), metadata); + extract(new SequentialByteArrayReader(segmentBytes), metadata, context); } } @@ -55,11 +57,11 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada * Performs the DHT tables extraction, adding found tables to the specified * instance of {@link Metadata}. */ - public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata) + public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, @NotNull MetadataContext context) { HuffmanTablesDirectory directory = metadata.getFirstDirectoryOfType(HuffmanTablesDirectory.class); if (directory == null) { - directory = new HuffmanTablesDirectory(); + directory = new HuffmanTablesDirectory(context); metadata.addDirectory(directory); } diff --git a/Source/com/drew/metadata/jpeg/JpegDirectory.java b/Source/com/drew/metadata/jpeg/JpegDirectory.java index 22201d364..f792129c8 100644 --- a/Source/com/drew/metadata/jpeg/JpegDirectory.java +++ b/Source/com/drew/metadata/jpeg/JpegDirectory.java @@ -23,6 +23,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataException; import java.util.HashMap; @@ -79,9 +80,9 @@ public class JpegDirectory extends Directory _tagNameMap.put(TAG_COMPONENT_DATA_4, "Component 4"); } - public JpegDirectory() + public JpegDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new JpegDescriptor(this)); + this.setDescriptor(new JpegDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/jpeg/JpegDnlReader.java b/Source/com/drew/metadata/jpeg/JpegDnlReader.java index 784a2ef16..50e06c0ec 100644 --- a/Source/com/drew/metadata/jpeg/JpegDnlReader.java +++ b/Source/com/drew/metadata/jpeg/JpegDnlReader.java @@ -27,6 +27,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.ErrorDirectory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import java.io.IOException; import java.util.Collections; @@ -44,14 +45,14 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.DNL); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { - extract(segmentBytes, metadata, segmentType); + extract(segmentBytes, metadata, segmentType, context); } } - public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType) + public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType, MetadataContext context) { JpegDirectory directory = metadata.getFirstDirectoryOfType(JpegDirectory.class); if (directory == null) { diff --git a/Source/com/drew/metadata/jpeg/JpegReader.java b/Source/com/drew/metadata/jpeg/JpegReader.java index 077ee21aa..973f69f02 100644 --- a/Source/com/drew/metadata/jpeg/JpegReader.java +++ b/Source/com/drew/metadata/jpeg/JpegReader.java @@ -25,7 +25,9 @@ import com.drew.lang.SequentialByteArrayReader; import com.drew.lang.SequentialReader; import com.drew.lang.annotations.NotNull; +import com.drew.lang.annotations.Nullable; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import java.io.IOException; import java.util.Arrays; @@ -62,16 +64,16 @@ public Iterable getSegmentTypes() ); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { for (byte[] segmentBytes : segments) { - extract(segmentBytes, metadata, segmentType); + extract(segmentBytes, metadata, segmentType, context); } } - public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType) + public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType, MetadataContext context) { - JpegDirectory directory = new JpegDirectory(); + JpegDirectory directory = new JpegDirectory(context); metadata.addDirectory(directory); // The value of TAG_COMPRESSION_TYPE is determined by the segment type found diff --git a/Source/com/drew/metadata/mov/atoms/canon/CanonThumbnailAtom.java b/Source/com/drew/metadata/mov/atoms/canon/CanonThumbnailAtom.java index c5a5d86c1..4e7d4a02e 100644 --- a/Source/com/drew/metadata/mov/atoms/canon/CanonThumbnailAtom.java +++ b/Source/com/drew/metadata/mov/atoms/canon/CanonThumbnailAtom.java @@ -15,6 +15,7 @@ import com.drew.lang.StreamReader; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.Tag; import com.drew.metadata.exif.ExifDirectoryBase; import com.drew.metadata.exif.ExifIFD0Directory; @@ -58,8 +59,10 @@ private void readCNDA(SequentialReader reader) throws IOException // TODO should we keep all extracted metadata here? Metadata metadata = new Metadata(); + // TODO document this default context? + MetadataContext context = new MetadataContext(); for (JpegSegmentType segmentType : exifReader.getSegmentTypes()) { - exifReader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType); + exifReader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType, context); } Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); diff --git a/Source/com/drew/metadata/photoshop/DuckyDirectory.java b/Source/com/drew/metadata/photoshop/DuckyDirectory.java index 9c6d69050..9edd58c7b 100644 --- a/Source/com/drew/metadata/photoshop/DuckyDirectory.java +++ b/Source/com/drew/metadata/photoshop/DuckyDirectory.java @@ -23,6 +23,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import java.util.HashMap; @@ -48,9 +49,9 @@ public class DuckyDirectory extends Directory _tagNameMap.put(TAG_COPYRIGHT, "Copyright"); } - public DuckyDirectory() + public DuckyDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new TagDescriptor(this)); + this.setDescriptor(new TagDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/photoshop/DuckyReader.java b/Source/com/drew/metadata/photoshop/DuckyReader.java index b8e90fad9..e0b01688c 100644 --- a/Source/com/drew/metadata/photoshop/DuckyReader.java +++ b/Source/com/drew/metadata/photoshop/DuckyReader.java @@ -27,6 +27,7 @@ import com.drew.lang.SequentialReader; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import java.io.IOException; import java.util.Collections; @@ -48,7 +49,7 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPC); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { final int preambleLength = JPEG_SEGMENT_PREAMBLE.length(); @@ -59,13 +60,14 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada extract( new SequentialByteArrayReader(segmentBytes, preambleLength), - metadata); + metadata, + context); } } - public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata) + public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, @NotNull MetadataContext context) { - DuckyDirectory directory = new DuckyDirectory(); + DuckyDirectory directory = new DuckyDirectory(context); metadata.addDirectory(directory); try diff --git a/Source/com/drew/metadata/photoshop/PhotoshopDescriptor.java b/Source/com/drew/metadata/photoshop/PhotoshopDescriptor.java index 9977994f5..2f5d46ae3 100644 --- a/Source/com/drew/metadata/photoshop/PhotoshopDescriptor.java +++ b/Source/com/drew/metadata/photoshop/PhotoshopDescriptor.java @@ -25,6 +25,7 @@ import com.drew.lang.RandomAccessReader; import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; import java.io.IOException; @@ -41,9 +42,9 @@ @SuppressWarnings("WeakerAccess") public class PhotoshopDescriptor extends TagDescriptor { - public PhotoshopDescriptor(@NotNull PhotoshopDirectory directory) + public PhotoshopDescriptor(@NotNull PhotoshopDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } @Override @@ -144,14 +145,14 @@ public String getJpegQualityString() format = "Progressive"; break; default: - format = String.format("Unknown 0x%04X", f); + format = String.format(getContext().locale(), "Unknown 0x%04X", f); } String scans = s >= 1 && s <= 3 - ? String.format("%d", s + 2) - : String.format("Unknown 0x%04X", s); + ? String.format(getContext().locale(), "%d", s + 2) + : String.format(getContext().locale(), "Unknown 0x%04X", s); - return String.format("%d (%s), %s format, %s scans", q1, quality, format, scans); + return String.format(getContext().locale(), "%d (%s), %s format, %s scans", q1, quality, format, scans); } catch (IOException e) { return null; } @@ -190,9 +191,9 @@ public String getPrintScaleDescription() case 1: return "Size to fit"; case 2: - return String.format("User defined, X:%s Y:%s, Scale:%s", locX, locY, scale); + return String.format(getContext().locale(), "User defined, X:%s Y:%s, Scale:%s", locX, locY, scale); default: - return String.format("Unknown %04X, X:%s Y:%s, Scale:%s", style, locX, locY, scale); + return String.format(getContext().locale(), "Unknown %04X, X:%s Y:%s, Scale:%s", style, locX, locY, scale); } } catch (Exception e) { return null; @@ -209,7 +210,7 @@ public String getResolutionInfoDescription() RandomAccessReader reader = new ByteArrayReader(bytes); float resX = reader.getS15Fixed16(0); float resY = reader.getS15Fixed16(8); // is this the correct offset? it's only reading 4 bytes each time - DecimalFormat format = new DecimalFormat("0.##"); + DecimalFormat format = new DecimalFormat("0.##", getDecimalFormatSymbols(getContext().locale())); return format.format(resX) + "x" + format.format(resY) + " DPI"; } catch (Exception e) { return null; @@ -237,7 +238,7 @@ public String getVersionDescription() String writerStr = reader.getString(pos, writerLength * 2, "UTF-16"); pos += writerLength * 2; int fileVersion = reader.getInt32(pos); - return String.format("%d (%s, %s) %d", ver, readerStr, writerStr, fileVersion); + return String.format(getContext().locale(), "%d (%s, %s) %d", ver, readerStr, writerStr, fileVersion); } catch (IOException e) { return null; } @@ -255,7 +256,7 @@ public String getSlicesDescription() String name = reader.getString(24, nameLength * 2, "UTF-16"); int pos = 24 + nameLength * 2; int sliceCount = reader.getInt32(pos); - return String.format("%s (%d,%d,%d,%d) %d Slices", + return String.format(getContext().locale(), "%s (%d,%d,%d,%d) %d Slices", name, reader.getInt32(4), reader.getInt32(8), reader.getInt32(12), reader.getInt32(16), sliceCount); } catch (IOException e) { return null; @@ -278,7 +279,7 @@ public String getThumbnailDescription(int tagType) int compSize = reader.getInt32(20); int bpp = reader.getInt32(24); //skip Number of planes - return String.format("%s, %dx%d, Decomp %d bytes, %d bpp, %d bytes", + return String.format(getContext().locale(), "%s, %dx%d, Decomp %d bytes, %d bpp, %d bytes", format == 1 ? "JpegRGB" : "RawRGB", width, height, totalSize, bpp, compSize); } catch (IOException e) { @@ -303,7 +304,7 @@ private String get32BitNumberString(int tag) return null; RandomAccessReader reader = new ByteArrayReader(bytes); try { - return String.format("%d", reader.getInt32(0)); + return String.format(getContext().locale(), "%d", reader.getInt32(0)); } catch (IOException e) { return null; } @@ -324,7 +325,7 @@ private String getBinaryDataString(int tagType) final byte[] bytes = _directory.getByteArray(tagType); if (bytes == null) return null; - return String.format("%d bytes binary data", bytes.length); + return String.format(getContext().locale(), "%d bytes binary data", bytes.length); } @Nullable diff --git a/Source/com/drew/metadata/photoshop/PhotoshopDirectory.java b/Source/com/drew/metadata/photoshop/PhotoshopDirectory.java index cc03e82b0..a3670cd2d 100644 --- a/Source/com/drew/metadata/photoshop/PhotoshopDirectory.java +++ b/Source/com/drew/metadata/photoshop/PhotoshopDirectory.java @@ -24,6 +24,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.util.HashMap; @@ -224,9 +225,9 @@ public class PhotoshopDirectory extends Directory _tagNameMap.put(TAG_PRINT_FLAGS_INFO, "Print Flags Information"); } - public PhotoshopDirectory() + public PhotoshopDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new PhotoshopDescriptor(this)); + this.setDescriptor(new PhotoshopDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/photoshop/PhotoshopReader.java b/Source/com/drew/metadata/photoshop/PhotoshopReader.java index 90ef156f7..9fe2510a2 100644 --- a/Source/com/drew/metadata/photoshop/PhotoshopReader.java +++ b/Source/com/drew/metadata/photoshop/PhotoshopReader.java @@ -30,6 +30,7 @@ import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.exif.ExifReader; import com.drew.metadata.icc.IccReader; import com.drew.metadata.iptc.IptcReader; @@ -58,7 +59,7 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPD); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { final int preambleLength = JPEG_SEGMENT_PREAMBLE.length(); @@ -70,18 +71,30 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada extract( new SequentialByteArrayReader(segmentBytes, preambleLength + 1), segmentBytes.length - preambleLength - 1, - metadata); + metadata, + context); } } public void extract(@NotNull final SequentialReader reader, int length, @NotNull final Metadata metadata) { - extract(reader, length, metadata, null); + extract(reader, length, metadata, null, null); } public void extract(@NotNull final SequentialReader reader, int length, @NotNull final Metadata metadata, @Nullable final Directory parentDirectory) { - PhotoshopDirectory directory = new PhotoshopDirectory(); + // TODO document this default context? + extract(reader, length, metadata, parentDirectory, new MetadataContext()); + } + + public void extract(@NotNull final SequentialReader reader, int length, @NotNull final Metadata metadata, @NotNull MetadataContext context) + { + extract(reader, length, metadata, null, context); + } + + public void extract(@NotNull final SequentialReader reader, int length, @NotNull final Metadata metadata, @Nullable final Directory parentDirectory, @NotNull MetadataContext context) + { + PhotoshopDirectory directory = new PhotoshopDirectory(context); metadata.addDirectory(directory); if (parentDirectory != null) @@ -145,13 +158,13 @@ public void extract(@NotNull final SequentialReader reader, int length, @NotNull if (signature.equals("8BIM")) { if (tagType == PhotoshopDirectory.TAG_IPTC) - new IptcReader().extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.length, directory); + new IptcReader().extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.length, directory, context); else if (tagType == PhotoshopDirectory.TAG_ICC_PROFILE_BYTES) - new IccReader().extract(new ByteArrayReader(tagBytes), metadata, directory); + new IccReader().extract(new ByteArrayReader(tagBytes), metadata, directory, context); else if (tagType == PhotoshopDirectory.TAG_EXIF_DATA_1 || tagType == PhotoshopDirectory.TAG_EXIF_DATA_3) - new ExifReader().extract(new ByteArrayReader(tagBytes), metadata, 0, directory); + new ExifReader().extract(new ByteArrayReader(tagBytes), metadata, 0, directory, context); else if (tagType == PhotoshopDirectory.TAG_XMP_DATA) - new XmpReader().extract(tagBytes, metadata, directory); + new XmpReader().extract(tagBytes, metadata, directory, context); else if (tagType >= 0x07D0 && tagType <= 0x0BB6) { clippingPathCount++; tagBytes = Arrays.copyOf(tagBytes, tagBytes.length + description.length() + 1); @@ -169,7 +182,7 @@ else if (tagType >= 0x07D0 && tagType <= 0x0BB6) { directory.setByteArray(tagType, tagBytes); if (tagType >= 0x0fa0 && tagType <= 0x1387) - PhotoshopDirectory._tagNameMap.put(tagType, String.format("Plug-in %d Data", tagType - 0x0fa0 + 1)); + PhotoshopDirectory._tagNameMap.put(tagType, String.format(context.locale(), "Plug-in %d Data", tagType - 0x0fa0 + 1)); } } catch (Exception ex) { directory.addError(ex.getMessage()); diff --git a/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java b/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java index a327bd808..f863667f4 100644 --- a/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java +++ b/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java @@ -27,8 +27,12 @@ import com.drew.metadata.Directory; import com.drew.metadata.ErrorDirectory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.StringValue; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Locale; import java.util.Stack; /** @@ -43,11 +47,13 @@ public abstract class DirectoryTiffHandler implements TiffHandler @Nullable private Directory _rootParentDirectory; @Nullable protected Directory _currentDirectory; protected final Metadata _metadata; + protected MetadataContext _context; - protected DirectoryTiffHandler(Metadata metadata, @Nullable Directory parentDirectory) + protected DirectoryTiffHandler(Metadata metadata, @Nullable Directory parentDirectory, @Nullable MetadataContext context) { _metadata = metadata; _rootParentDirectory = parentDirectory; + _context = context; } public void endingIFD() @@ -57,15 +63,7 @@ public void endingIFD() protected void pushDirectory(@NotNull Class directoryClass) { - Directory newDirectory; - - try { - newDirectory = directoryClass.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } + Directory newDirectory = getDirectoryInstance(directoryClass); // If this is the first directory, don't add to the stack if (_currentDirectory == null) { @@ -85,6 +83,29 @@ protected void pushDirectory(@NotNull Class directoryClass) _metadata.addDirectory(_currentDirectory); } + private Directory getDirectoryInstance(Class directoryClass) { + Constructor[] constructors = directoryClass.getConstructors(); + + try { + // pass context to directory, if it has a constructor that can receive it + // TODO improve? + for (Constructor constructor : constructors) { + Class[] parameterTypes = constructor.getParameterTypes(); + if (parameterTypes.length == 1 && parameterTypes[0] == MetadataContext.class) { + return (Directory) constructor.newInstance(_context); + } + } + + return directoryClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + public void warn(@NotNull String message) { getCurrentOrErrorDirectory().addError(message); diff --git a/Source/com/drew/metadata/xmp/XmpDescriptor.java b/Source/com/drew/metadata/xmp/XmpDescriptor.java index 0158c61af..dd5893bdf 100644 --- a/Source/com/drew/metadata/xmp/XmpDescriptor.java +++ b/Source/com/drew/metadata/xmp/XmpDescriptor.java @@ -21,6 +21,7 @@ package com.drew.metadata.xmp; import com.drew.lang.annotations.NotNull; +import com.drew.metadata.MetadataContext; import com.drew.metadata.TagDescriptor; /** @@ -32,8 +33,8 @@ @SuppressWarnings("WeakerAccess") public class XmpDescriptor extends TagDescriptor { - public XmpDescriptor(@NotNull XmpDirectory directory) + public XmpDescriptor(@NotNull XmpDirectory directory, @NotNull MetadataContext context) { - super(directory); + super(directory, context); } } diff --git a/Source/com/drew/metadata/xmp/XmpDirectory.java b/Source/com/drew/metadata/xmp/XmpDirectory.java index 4824d5f74..41c78bf60 100644 --- a/Source/com/drew/metadata/xmp/XmpDirectory.java +++ b/Source/com/drew/metadata/xmp/XmpDirectory.java @@ -29,6 +29,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Directory; +import com.drew.metadata.MetadataContext; import java.util.Collections; import java.util.HashMap; @@ -59,9 +60,9 @@ public class XmpDirectory extends Directory @Nullable private XMPMeta _xmpMeta; - public XmpDirectory() + public XmpDirectory(@NotNull MetadataContext context) { - this.setDescriptor(new XmpDescriptor(this)); + this.setDescriptor(new XmpDescriptor(this, context)); } @Override diff --git a/Source/com/drew/metadata/xmp/XmpReader.java b/Source/com/drew/metadata/xmp/XmpReader.java index 0935ee6ce..346814414 100644 --- a/Source/com/drew/metadata/xmp/XmpReader.java +++ b/Source/com/drew/metadata/xmp/XmpReader.java @@ -35,6 +35,7 @@ import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.StringValue; import java.io.IOException; @@ -65,7 +66,7 @@ public class XmpReader implements JpegSegmentMetadataReader private static final String SCHEMA_XMP_NOTES = "http://ns.adobe.com/xmp/note/"; @NotNull private static final String ATTRIBUTE_EXTENDED_XMP = "xmpNote:HasExtendedXMP"; - // Limit photoshop:DocumentAncestors node as it can reach over 100000 items and make parsing extremely slow. + // Limit photoshop:DocumentAncestors node as it can reach over 100000 items and make parsing extremely slow. // This is not a typical value but it may happen https://forums.adobe.com/thread/2081839 @NotNull private static final ParseOptions PARSE_OPTIONS = new ParseOptions().setXMPNodesToLimit(Collections.singletonMap("photoshop:DocumentAncestors", 1000)); @@ -85,12 +86,12 @@ public Iterable getSegmentTypes() /** * Version specifically for dealing with XMP found in JPEG segments. This form of XMP has a peculiar preamble, which * must be removed before parsing the XML. - * * @param segments The byte array from which the metadata should be extracted. * @param metadata The {@link Metadata} object into which extracted values should be merged. * @param segmentType The {@link JpegSegmentType} being read. + * @param context The {@link MetadataContext} to use for parsing and formatting. */ - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType, @NotNull MetadataContext context) { final int preambleLength = XMP_JPEG_PREAMBLE.length(); final int extensionPreambleLength = XMP_EXTENSION_JPEG_PREAMBLE.length(); @@ -120,13 +121,13 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada segmentBytes.length >= extensionPreambleLength && XMP_EXTENSION_JPEG_PREAMBLE.equalsIgnoreCase(new String(segmentBytes, 0, extensionPreambleLength))) { - extendedXMPBuffer = processExtendedXMPChunk(metadata, segmentBytes, extendedXMPGUID, extendedXMPBuffer); + extendedXMPBuffer = processExtendedXMPChunk(metadata, segmentBytes, extendedXMPGUID, extendedXMPBuffer, context); } } // Now that the Extended XMP chunks have been concatenated, let's parse and merge with the Standard XMP. if (extendedXMPBuffer != null) { - extract(extendedXMPBuffer, metadata); + extract(extendedXMPBuffer, metadata, context); } } @@ -137,7 +138,8 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada */ public void extract(@NotNull final byte[] xmpBytes, @NotNull Metadata metadata) { - extract(xmpBytes, metadata, null); + // TODO document this default context? + extract(xmpBytes, metadata, null, new MetadataContext()); } /** @@ -147,7 +149,28 @@ public void extract(@NotNull final byte[] xmpBytes, @NotNull Metadata metadata) */ public void extract(@NotNull final byte[] xmpBytes, @NotNull Metadata metadata, @Nullable Directory parentDirectory) { - extract(xmpBytes, 0, xmpBytes.length, metadata, parentDirectory); + // TODO document this default context? + extract(xmpBytes, metadata, parentDirectory, new MetadataContext()); + } + + /** + * Performs the XMP data extraction, adding found values to the specified instance of {@link Metadata}. + *

+ * The extraction is done with Adobe's XMPCore library. + */ + public void extract(@NotNull final byte[] xmpBytes, @NotNull Metadata metadata, @NotNull MetadataContext context) + { + extract(xmpBytes, metadata, null, context); + } + + /** + * Performs the XMP data extraction, adding found values to the specified instance of {@link Metadata}. + *

+ * The extraction is done with Adobe's XMPCore library. + */ + public void extract(@NotNull final byte[] xmpBytes, @NotNull Metadata metadata, @Nullable Directory parentDirectory, @NotNull MetadataContext context) + { + extract(xmpBytes, 0, xmpBytes.length, metadata, parentDirectory, context); } /** @@ -157,7 +180,18 @@ public void extract(@NotNull final byte[] xmpBytes, @NotNull Metadata metadata, */ public void extract(@NotNull final byte[] xmpBytes, int offset, int length, @NotNull Metadata metadata, @Nullable Directory parentDirectory) { - XmpDirectory directory = new XmpDirectory(); + // TODO document this default context? + extract(xmpBytes, offset, length, metadata, parentDirectory, new MetadataContext()); + } + + /** + * Performs the XMP data extraction, adding found values to the specified instance of {@link Metadata}. + *

+ * The extraction is done with Adobe's XMPCore library. + */ + public void extract(@NotNull final byte[] xmpBytes, int offset, int length, @NotNull Metadata metadata, @Nullable Directory parentDirectory, @NotNull MetadataContext context) + { + XmpDirectory directory = new XmpDirectory(context); if (parentDirectory != null) directory.setParent(parentDirectory); @@ -189,7 +223,8 @@ public void extract(@NotNull final byte[] xmpBytes, int offset, int length, @Not */ public void extract(@NotNull final String xmpString, @NotNull Metadata metadata) { - extract(xmpString, metadata, null); + // TODO document this default context? + extract(xmpString, metadata, null, new MetadataContext()); } /** @@ -199,7 +234,8 @@ public void extract(@NotNull final String xmpString, @NotNull Metadata metadata) */ public void extract(@NotNull final StringValue xmpString, @NotNull Metadata metadata) { - extract(xmpString.getBytes(), metadata, null); + // TODO document this default context? + extract(xmpString.getBytes(), metadata, null, new MetadataContext()); } /** @@ -207,9 +243,9 @@ public void extract(@NotNull final StringValue xmpString, @NotNull Metadata meta *

* The extraction is done with Adobe's XMPCore library. */ - public void extract(@NotNull final String xmpString, @NotNull Metadata metadata, @Nullable Directory parentDirectory) + public void extract(@NotNull final String xmpString, @NotNull Metadata metadata, @Nullable Directory parentDirectory, @NotNull MetadataContext context) { - XmpDirectory directory = new XmpDirectory(); + XmpDirectory directory = new XmpDirectory(context); if (parentDirectory != null) directory.setParent(parentDirectory); @@ -264,7 +300,7 @@ private static String getExtendedXMPGUID(@NotNull Metadata metadata) * at page 19 */ @Nullable - private static byte[] processExtendedXMPChunk(@NotNull Metadata metadata, @NotNull byte[] segmentBytes, @NotNull String extendedXMPGUID, @Nullable byte[] extendedXMPBuffer) + private static byte[] processExtendedXMPChunk(@NotNull Metadata metadata, @NotNull byte[] segmentBytes, @NotNull String extendedXMPGUID, @Nullable byte[] extendedXMPBuffer, @NotNull MetadataContext context) { final int extensionPreambleLength = XMP_EXTENSION_JPEG_PREAMBLE.length(); final int segmentLength = segmentBytes.length; @@ -295,13 +331,13 @@ private static byte[] processExtendedXMPChunk(@NotNull Metadata metadata, @NotNu if (extendedXMPBuffer.length == fullLength) { System.arraycopy(segmentBytes, totalOffset, extendedXMPBuffer, chunkOffset, segmentLength - totalOffset); } else { - XmpDirectory directory = new XmpDirectory(); - directory.addError(String.format("Inconsistent length for the Extended XMP buffer: %d instead of %d", fullLength, extendedXMPBuffer.length)); + XmpDirectory directory = new XmpDirectory(context); + directory.addError(String.format(context.locale(), "Inconsistent length for the Extended XMP buffer: %d instead of %d", fullLength, extendedXMPBuffer.length)); metadata.addDirectory(directory); } } } catch (IOException ex) { - XmpDirectory directory = new XmpDirectory(); + XmpDirectory directory = new XmpDirectory(context); directory.addError(ex.getMessage()); metadata.addDirectory(directory); } diff --git a/Tests/com/drew/imaging/jpeg/JpegMetadataReaderTest.java b/Tests/com/drew/imaging/jpeg/JpegMetadataReaderTest.java index 3327783d9..fa3c2daa4 100644 --- a/Tests/com/drew/imaging/jpeg/JpegMetadataReaderTest.java +++ b/Tests/com/drew/imaging/jpeg/JpegMetadataReaderTest.java @@ -22,7 +22,10 @@ import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; +import com.drew.metadata.exif.ExifDirectoryBase; import com.drew.metadata.exif.ExifSubIFDDirectory; +import com.drew.metadata.exif.GpsDirectory; import com.drew.metadata.jpeg.HuffmanTablesDirectory; import com.drew.metadata.jpeg.HuffmanTablesDirectory.HuffmanTable; import com.drew.metadata.xmp.XmpDirectory; @@ -30,6 +33,7 @@ import java.io.File; import java.io.FileInputStream; +import java.util.Locale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -94,4 +98,48 @@ private void validate(Metadata metadata) assertNotNull(directory); assertTrue(((HuffmanTablesDirectory) directory).isOptimized()); } + + @Test + public void testConfigurableLocaleEnglish() throws Exception { + Locale defaultLocale = Locale.getDefault(); + try { + // set default Locale to Dutch, configure English -> expect period + Locale.setDefault(new Locale("nl")); + MetadataContext context = new MetadataContext().locale(Locale.ENGLISH); + Metadata metadata = JpegMetadataReader.readMetadata(new File("Tests/Data/withIptcExifGps.jpg"), context); + + Directory gps = metadata.getFirstDirectoryOfType(GpsDirectory.class); + assertEquals("54° 59' 22.8\"", gps.getDescription(GpsDirectory.TAG_LATITUDE)); + assertEquals("-1° 54' 51\"", gps.getDescription(GpsDirectory.TAG_LONGITUDE)); + + Directory subIFD = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class); + assertEquals("f/0.6", subIFD.getDescription(ExifDirectoryBase.TAG_FNUMBER)); + } + finally { + // reset default locale + Locale.setDefault(defaultLocale); + } + } + + @Test + public void testConfigurableLocaleDutch() throws Exception { + Locale defaultLocale = Locale.getDefault(); + try { + // set default Locale to English, configure Dutch -> expect comma + Locale.setDefault(Locale.ENGLISH); + MetadataContext context = new MetadataContext().locale(new Locale("nl")); + Metadata metadata = JpegMetadataReader.readMetadata(new File("Tests/Data/withIptcExifGps.jpg"), context); + + Directory gps = metadata.getFirstDirectoryOfType(GpsDirectory.class); + assertEquals("54° 59' 22,8\"", gps.getDescription(GpsDirectory.TAG_LATITUDE)); + assertEquals("-1° 54' 51\"", gps.getDescription(GpsDirectory.TAG_LONGITUDE)); + + Directory subIFD = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class); + assertEquals("f/0,6", subIFD.getDescription(ExifDirectoryBase.TAG_FNUMBER)); + } + finally { + // reset default locale + Locale.setDefault(defaultLocale); + } + } } diff --git a/Tests/com/drew/metadata/MetadataTest.java b/Tests/com/drew/metadata/MetadataTest.java index 7a10e52a2..d258e1011 100644 --- a/Tests/com/drew/metadata/MetadataTest.java +++ b/Tests/com/drew/metadata/MetadataTest.java @@ -38,6 +38,12 @@ */ public class MetadataTest { + private MetadataContext _context; + + public void setUp() { + _context = new MetadataContext(); + } + @Test public void testGetDirectoryWhenNotExists() { @@ -47,7 +53,7 @@ public void testGetDirectoryWhenNotExists() @Test public void testHasErrors() throws Exception { - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.addError("Test Error 1"); Metadata metadata = new Metadata(); @@ -66,7 +72,7 @@ public void testToString() metadata.addDirectory(new ExifIFD0Directory()); assertEquals("Metadata (1 directory)", metadata.toString()); - metadata.addDirectory(new ExifSubIFDDirectory()); + metadata.addDirectory(new ExifSubIFDDirectory(_context)); assertEquals("Metadata (2 directories)", metadata.toString()); } @@ -74,9 +80,9 @@ public void testToString() public void testOrderOfSameType() { Metadata metadata = new Metadata(); - Directory directory2 = new ExifSubIFDDirectory(); - Directory directory3 = new ExifSubIFDDirectory(); - Directory directory1 = new ExifSubIFDDirectory(); + Directory directory2 = new ExifSubIFDDirectory(_context); + Directory directory3 = new ExifSubIFDDirectory(_context); + Directory directory1 = new ExifSubIFDDirectory(_context); metadata.addDirectory(directory1); metadata.addDirectory(directory2); @@ -95,7 +101,7 @@ public void testOrderOfSameType() public void testOrderOfDifferentTypes() { Metadata metadata = new Metadata(); - Directory directory1 = new ExifSubIFDDirectory(); + Directory directory1 = new ExifSubIFDDirectory(_context); Directory directory2 = new ExifThumbnailDirectory(); Directory directory3 = new ExifIFD0Directory(); diff --git a/Tests/com/drew/metadata/adobe/AdobeJpegReaderTest.java b/Tests/com/drew/metadata/adobe/AdobeJpegReaderTest.java index ee9baf3b7..4ee46a0d2 100644 --- a/Tests/com/drew/metadata/adobe/AdobeJpegReaderTest.java +++ b/Tests/com/drew/metadata/adobe/AdobeJpegReaderTest.java @@ -25,6 +25,7 @@ import com.drew.lang.SequentialByteArrayReader; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.tools.FileUtil; import org.junit.Test; @@ -42,7 +43,8 @@ public class AdobeJpegReaderTest public static AdobeJpegDirectory processBytes(@NotNull String filePath) throws IOException { Metadata metadata = new Metadata(); - new AdobeJpegReader().extract(new SequentialByteArrayReader(FileUtil.readBytes(filePath)), metadata); + MetadataContext context = new MetadataContext(); + new AdobeJpegReader().extract(new SequentialByteArrayReader(FileUtil.readBytes(filePath)), metadata, context); AdobeJpegDirectory directory = metadata.getFirstDirectoryOfType(AdobeJpegDirectory.class); assertNotNull(directory); diff --git a/Tests/com/drew/metadata/exif/ExifDirectoryTest.java b/Tests/com/drew/metadata/exif/ExifDirectoryTest.java index d818dcbac..803660fb6 100644 --- a/Tests/com/drew/metadata/exif/ExifDirectoryTest.java +++ b/Tests/com/drew/metadata/exif/ExifDirectoryTest.java @@ -24,7 +24,9 @@ import com.drew.lang.GeoLocation; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataException; +import org.junit.Before; import org.junit.Test; import java.io.IOException; @@ -41,13 +43,20 @@ @SuppressWarnings("ConstantConditions") public class ExifDirectoryTest { + private MetadataContext _context; + + @Before + public void setUp() { + _context = new MetadataContext(); + } + @Test public void testGetDirectoryName() throws Exception { - Directory subIFDDirectory = new ExifSubIFDDirectory(); + Directory subIFDDirectory = new ExifSubIFDDirectory(_context); Directory ifd0Directory = new ExifIFD0Directory(); Directory thumbDirectory = new ExifThumbnailDirectory(); - Directory gpsDirectory = new GpsDirectory(); + Directory gpsDirectory = new GpsDirectory(_context); assertFalse(subIFDDirectory.hasErrors()); assertFalse(ifd0Directory.hasErrors()); diff --git a/Tests/com/drew/metadata/exif/ExifReaderTest.java b/Tests/com/drew/metadata/exif/ExifReaderTest.java index 3cd95547b..144abe7ce 100644 --- a/Tests/com/drew/metadata/exif/ExifReaderTest.java +++ b/Tests/com/drew/metadata/exif/ExifReaderTest.java @@ -26,7 +26,9 @@ import com.drew.lang.annotations.NotNull; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.tools.FileUtil; +import org.junit.Before; import org.junit.Test; import java.io.IOException; @@ -46,7 +48,7 @@ public static Metadata processBytes(@NotNull String filePath) throws IOException { Metadata metadata = new Metadata(); byte[] bytes = FileUtil.readBytes(filePath); - new ExifReader().extract(new ByteArrayReader(bytes), metadata, ExifReader.JPEG_SEGMENT_PREAMBLE.length(), null); + new ExifReader().extract(new ByteArrayReader(bytes), metadata, ExifReader.JPEG_SEGMENT_PREAMBLE.length()); return metadata; } @@ -63,7 +65,7 @@ public static T processBytes(@NotNull String filePath, @No public void testExtractWithNullDataThrows() throws Exception { try{ - new ExifReader().readJpegSegments(null, new Metadata(), JpegSegmentType.APP1); + new ExifReader().readJpegSegments(null, new Metadata(), JpegSegmentType.APP1, new MetadataContext()); fail("Exception expected"); } catch (NullPointerException npe) { // passed @@ -87,9 +89,10 @@ public void testReadJpegSegmentWithNoExifData() throws Exception { byte[] badExifData = new byte[]{ 1,2,3,4,5,6,7,8,9,10 }; Metadata metadata = new Metadata(); + MetadataContext context = new MetadataContext(); ArrayList segments = new ArrayList(); segments.add(badExifData); - new ExifReader().readJpegSegments(segments, metadata, JpegSegmentType.APP1); + new ExifReader().readJpegSegments(segments, metadata, JpegSegmentType.APP1, context); assertEquals(0, metadata.getDirectoryCount()); assertFalse(metadata.hasErrors()); } diff --git a/Tests/com/drew/metadata/exif/ExifSubIFDDescriptorTest.java b/Tests/com/drew/metadata/exif/ExifSubIFDDescriptorTest.java index e05dc5afd..7b72b58f9 100644 --- a/Tests/com/drew/metadata/exif/ExifSubIFDDescriptorTest.java +++ b/Tests/com/drew/metadata/exif/ExifSubIFDDescriptorTest.java @@ -20,6 +20,8 @@ */ package com.drew.metadata.exif; +import com.drew.metadata.MetadataContext; +import org.junit.Before; import org.junit.Test; import static com.drew.metadata.exif.ExifSubIFDDirectory.*; @@ -32,13 +34,21 @@ */ public class ExifSubIFDDescriptorTest { + private MetadataContext _context; + + @Before + public void setUp() + { + _context = new MetadataContext(); + } + @Test public void testUserCommentDescription_EmptyEncoding() throws Exception { byte[] commentBytes = "\0\0\0\0\0\0\0\0This is a comment".getBytes(); - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("This is a comment", descriptor.getDescription(TAG_USER_COMMENT)); } @@ -46,9 +56,9 @@ public void testUserCommentDescription_EmptyEncoding() throws Exception public void testUserCommentDescription_AsciiHeaderAsciiEncoding() throws Exception { byte[] commentBytes = "ASCII\0\0This is a comment".getBytes(); - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("This is a comment", descriptor.getDescription(TAG_USER_COMMENT)); } @@ -56,9 +66,9 @@ public void testUserCommentDescription_AsciiHeaderAsciiEncoding() throws Excepti public void testUserCommentDescription_BlankAscii() throws Exception { byte[] commentBytes = "ASCII\0\0\0 ".getBytes(); - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("", descriptor.getDescription(TAG_USER_COMMENT)); } @@ -67,9 +77,9 @@ public void testUserCommentDescription_ZeroLengthAscii1() throws Exception { // the 10-byte encoding region is only partially full byte[] commentBytes = "ASCII\0\0\0".getBytes(); - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("ASCII", descriptor.getDescription(TAG_USER_COMMENT)); } @@ -78,9 +88,9 @@ public void testUserCommentDescription_ZeroLengthAscii2() throws Exception { // fill the 10-byte encoding region byte[] commentBytes = "ASCII\0\0\0\0\0".getBytes(); - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("", descriptor.getDescription(TAG_USER_COMMENT)); } @@ -88,9 +98,9 @@ public void testUserCommentDescription_ZeroLengthAscii2() throws Exception public void testUnicodeComment_ActualBytes() throws Exception { byte[] commentBytes = new byte[] { 85, 78, 73, 67, 79, 68, 69, 0, 84, 0, 104, 0, 105, 0, 115, 0, 32, 0, 109, 0, 97, 0, 114, 0, 109, 0, 111, 0, 116, 0, 32, 0, 105, 0, 115, 0, 32, 0, 103, 0, 101, 0, 116, 0, 116, 0, 105, 0, 110, 0, 103, 0, 32, 0, 99, 0, 108, 0, 111, 0, 115, 0, 101, 0, 46, 0, 46, 0, 46, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0 }; - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("This marmot is getting close...", descriptor.getDescription(TAG_USER_COMMENT)); } @@ -98,9 +108,9 @@ public void testUnicodeComment_ActualBytes() throws Exception public void testUnicodeComment_Ascii() throws Exception { byte[] commentBytes = new byte[] { 65, 83, 67, 73, 73, 0, 0, 0, 73, 32, 97, 109, 32, 97, 32, 99, 111, 109, 109, 101, 110, 116, 46, 32, 89, 101, 121, 46, 0 }; - ExifSubIFDDirectory directory = new ExifSubIFDDirectory(); + ExifSubIFDDirectory directory = new ExifSubIFDDirectory(_context); directory.setByteArray(TAG_USER_COMMENT, commentBytes); - ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory); + ExifSubIFDDescriptor descriptor = new ExifSubIFDDescriptor(directory, _context); assertEquals("I am a comment. Yey.", descriptor.getDescription(TAG_USER_COMMENT)); } } diff --git a/Tests/com/drew/metadata/icc/IccReaderTest.java b/Tests/com/drew/metadata/icc/IccReaderTest.java index 6ed5e5678..087a01a74 100644 --- a/Tests/com/drew/metadata/icc/IccReaderTest.java +++ b/Tests/com/drew/metadata/icc/IccReaderTest.java @@ -21,14 +21,22 @@ package com.drew.metadata.icc; +import com.drew.imaging.jpeg.JpegMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; +import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; +import com.drew.metadata.exif.ExifDirectoryBase; +import com.drew.metadata.exif.ExifSubIFDDirectory; +import com.drew.metadata.exif.GpsDirectory; import com.drew.testing.TestHelper; import com.drew.tools.FileUtil; import org.junit.Test; +import java.io.File; import java.util.Arrays; +import java.util.Locale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -62,7 +70,8 @@ public void testReadJpegSegments_InvalidData() throws Exception byte[] app2Bytes = FileUtil.readBytes("Tests/Data/iccDataInvalid1.jpg.app2"); Metadata metadata = new Metadata(); - new IccReader().readJpegSegments(Arrays.asList(app2Bytes), metadata, JpegSegmentType.APP2); + MetadataContext context = new MetadataContext(); + new IccReader().readJpegSegments(Arrays.asList(app2Bytes), metadata, JpegSegmentType.APP2, context); IccDirectory directory = metadata.getFirstDirectoryOfType(IccDirectory.class); @@ -76,7 +85,8 @@ public void testExtract_ProfileDateTime() throws Exception byte[] app2Bytes = FileUtil.readBytes("Tests/Data/withExifAndIptc.jpg.app2"); Metadata metadata = new Metadata(); - new IccReader().readJpegSegments(Arrays.asList(app2Bytes), metadata, JpegSegmentType.APP2); + MetadataContext context = new MetadataContext(); + new IccReader().readJpegSegments(Arrays.asList(app2Bytes), metadata, JpegSegmentType.APP2, context); IccDirectory directory = metadata.getFirstDirectoryOfType(IccDirectory.class); @@ -84,4 +94,40 @@ public void testExtract_ProfileDateTime() throws Exception assertEquals("1998:02:09 06:49:00", directory.getString(IccDirectory.TAG_PROFILE_DATETIME)); assertEquals(887006940000L, directory.getDate(IccDirectory.TAG_PROFILE_DATETIME).getTime()); } + + @Test + public void testConfigurableLocaleEnglish() throws Exception { + Locale defaultLocale = Locale.getDefault(); + try { + // set default Locale to Dutch, configure English -> expect period + Locale.setDefault(new Locale("nl")); + MetadataContext context = new MetadataContext().locale(Locale.ENGLISH); + Metadata metadata = JpegMetadataReader.readMetadata(new File("Tests/Data/withIptcExifGps.jpg"), context); + + Directory icc = metadata.getFirstDirectoryOfType(IccDirectory.class); + assertEquals("0.964 1 0.825", icc.getString(IccDirectory.TAG_XYZ_VALUES)); + } + finally { + // reset default locale + Locale.setDefault(defaultLocale); + } + } + + @Test + public void testConfigurableLocaleDutch() throws Exception { + Locale defaultLocale = Locale.getDefault(); + try { + // set default Locale to English, configure Dutch -> expect comma + Locale.setDefault(Locale.ENGLISH); + MetadataContext context = new MetadataContext().locale(new Locale("nl")); + Metadata metadata = JpegMetadataReader.readMetadata(new File("Tests/Data/withIptcExifGps.jpg"), context); + + Directory icc = metadata.getFirstDirectoryOfType(IccDirectory.class); + assertEquals("0,964 1 0,825", icc.getString(IccDirectory.TAG_XYZ_VALUES)); + } + finally { + // reset default locale + Locale.setDefault(defaultLocale); + } + } } diff --git a/Tests/com/drew/metadata/iptc/IptcDirectoryTest.java b/Tests/com/drew/metadata/iptc/IptcDirectoryTest.java index c7a14f4ca..6a8a42f47 100644 --- a/Tests/com/drew/metadata/iptc/IptcDirectoryTest.java +++ b/Tests/com/drew/metadata/iptc/IptcDirectoryTest.java @@ -21,6 +21,7 @@ package com.drew.metadata.iptc; +import com.drew.metadata.MetadataContext; import org.junit.Before; import org.junit.Test; @@ -39,7 +40,7 @@ public class IptcDirectoryTest @Before public void setUp() { - _directory = new IptcDirectory(); + _directory = new IptcDirectory(new MetadataContext()); } @Test diff --git a/Tests/com/drew/metadata/jpeg/HuffmanTablesDescriptorTest.java b/Tests/com/drew/metadata/jpeg/HuffmanTablesDescriptorTest.java index 19f59d273..16c138e90 100644 --- a/Tests/com/drew/metadata/jpeg/HuffmanTablesDescriptorTest.java +++ b/Tests/com/drew/metadata/jpeg/HuffmanTablesDescriptorTest.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.metadata.MetadataContext; import org.junit.Before; import org.junit.Test; @@ -38,8 +39,9 @@ public class HuffmanTablesDescriptorTest @Before public void setUp() throws Exception { - _directory = new HuffmanTablesDirectory(); - _descriptor = new HuffmanTablesDescriptor(_directory); + MetadataContext context = new MetadataContext(); + _directory = new HuffmanTablesDirectory(context); + _descriptor = new HuffmanTablesDescriptor(_directory, context); } @Test diff --git a/Tests/com/drew/metadata/jpeg/HuffmanTablesDirectoryTest.java b/Tests/com/drew/metadata/jpeg/HuffmanTablesDirectoryTest.java index 288e9dd69..0bfaf5cdc 100644 --- a/Tests/com/drew/metadata/jpeg/HuffmanTablesDirectoryTest.java +++ b/Tests/com/drew/metadata/jpeg/HuffmanTablesDirectoryTest.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.metadata.MetadataContext; import org.junit.Before; import org.junit.Test; @@ -38,7 +39,7 @@ public class HuffmanTablesDirectoryTest @Before public void setUp() { - _directory = new HuffmanTablesDirectory(); + _directory = new HuffmanTablesDirectory(new MetadataContext()); } @Test diff --git a/Tests/com/drew/metadata/jpeg/JpegDescriptorTest.java b/Tests/com/drew/metadata/jpeg/JpegDescriptorTest.java index 357cd96d7..1d81ae265 100644 --- a/Tests/com/drew/metadata/jpeg/JpegDescriptorTest.java +++ b/Tests/com/drew/metadata/jpeg/JpegDescriptorTest.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.metadata.MetadataContext; import com.drew.metadata.MetadataException; import org.junit.Before; import org.junit.Test; @@ -39,8 +40,9 @@ public class JpegDescriptorTest @Before public void setUp() throws Exception { - _directory = new JpegDirectory(); - _descriptor = new JpegDescriptor(_directory); + MetadataContext context = new MetadataContext(); + _directory = new JpegDirectory(context); + _descriptor = new JpegDescriptor(_directory, context); } @Test diff --git a/Tests/com/drew/metadata/jpeg/JpegDhtReaderTest.java b/Tests/com/drew/metadata/jpeg/JpegDhtReaderTest.java index 97fdf58d9..2203ede5e 100644 --- a/Tests/com/drew/metadata/jpeg/JpegDhtReaderTest.java +++ b/Tests/com/drew/metadata/jpeg/JpegDhtReaderTest.java @@ -26,6 +26,7 @@ import com.drew.lang.SequentialByteArrayReader; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.metadata.jpeg.HuffmanTablesDirectory.HuffmanTable.HuffmanTableClass; import org.junit.Before; import org.junit.Test; @@ -43,13 +44,14 @@ public class JpegDhtReaderTest public static HuffmanTablesDirectory processBytes(String filePath) throws Exception { Metadata metadata = new Metadata(); + MetadataContext context = new MetadataContext(); JpegSegmentData segmentData = JpegSegmentReader.readSegments( new File(filePath), Collections.singletonList(JpegSegmentType.DHT)); Iterable segments = segmentData.getSegments(JpegSegmentType.DHT); for (byte[] segment : segments) { - new JpegDhtReader().extract(new SequentialByteArrayReader(segment), metadata); + new JpegDhtReader().extract(new SequentialByteArrayReader(segment), metadata, context); } diff --git a/Tests/com/drew/metadata/jpeg/JpegDirectoryTest.java b/Tests/com/drew/metadata/jpeg/JpegDirectoryTest.java index a7c4c131d..b036440ff 100644 --- a/Tests/com/drew/metadata/jpeg/JpegDirectoryTest.java +++ b/Tests/com/drew/metadata/jpeg/JpegDirectoryTest.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.metadata.MetadataContext; import org.junit.Before; import org.junit.Test; @@ -35,7 +36,7 @@ public class JpegDirectoryTest @Before public void setUp() { - _directory = new JpegDirectory(); + _directory = new JpegDirectory(new MetadataContext()); } @Test diff --git a/Tests/com/drew/metadata/jpeg/JpegReaderTest.java b/Tests/com/drew/metadata/jpeg/JpegReaderTest.java index f0e87a15d..1d35c7470 100644 --- a/Tests/com/drew/metadata/jpeg/JpegReaderTest.java +++ b/Tests/com/drew/metadata/jpeg/JpegReaderTest.java @@ -24,6 +24,7 @@ import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.tools.FileUtil; import org.junit.Before; import org.junit.Test; @@ -42,7 +43,8 @@ public class JpegReaderTest public static JpegDirectory processBytes(String filePath) throws IOException { Metadata metadata = new Metadata(); - new JpegReader().extract(FileUtil.readBytes(filePath), metadata, JpegSegmentType.SOF0); + MetadataContext context = new MetadataContext(); + new JpegReader().extract(FileUtil.readBytes(filePath), metadata, JpegSegmentType.SOF0, context); JpegDirectory directory = metadata.getFirstDirectoryOfType(JpegDirectory.class); assertNotNull(directory); diff --git a/Tests/com/drew/metadata/xmp/XmpReaderTest.java b/Tests/com/drew/metadata/xmp/XmpReaderTest.java index e5c81b5bc..47243bf7d 100644 --- a/Tests/com/drew/metadata/xmp/XmpReaderTest.java +++ b/Tests/com/drew/metadata/xmp/XmpReaderTest.java @@ -22,6 +22,7 @@ import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataContext; import com.drew.tools.FileUtil; import org.junit.Before; import org.junit.Test; @@ -41,9 +42,10 @@ public class XmpReaderTest public void setUp() throws Exception { Metadata metadata = new Metadata(); + MetadataContext context = new MetadataContext(); List jpegSegments = new ArrayList(); jpegSegments.add(FileUtil.readBytes("Tests/Data/withXmpAndIptc.jpg.app1.1")); - new XmpReader().readJpegSegments(jpegSegments, metadata, JpegSegmentType.APP1); + new XmpReader().readJpegSegments(jpegSegments, metadata, JpegSegmentType.APP1, context); Collection xmpDirectories = metadata.getDirectoriesOfType(XmpDirectory.class);