[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[626] | 2 | package org.openstreetmap.josm.tools;
|
---|
| 3 |
|
---|
[8132] | 4 | import java.awt.geom.AffineTransform;
|
---|
[626] | 5 | import java.io.File;
|
---|
[6127] | 6 | import java.io.IOException;
|
---|
[14159] | 7 | import java.time.DateTimeException;
|
---|
[17715] | 8 | import java.time.Instant;
|
---|
[626] | 9 | import java.util.Date;
|
---|
[15219] | 10 | import java.util.List;
|
---|
[11288] | 11 | import java.util.concurrent.TimeUnit;
|
---|
[626] | 12 |
|
---|
[11745] | 13 | import org.openstreetmap.josm.data.SystemOfMeasurement;
|
---|
[6209] | 14 | import org.openstreetmap.josm.data.coor.LatLon;
|
---|
[9383] | 15 | import org.openstreetmap.josm.tools.date.DateUtils;
|
---|
[6209] | 16 |
|
---|
[626] | 17 | import com.drew.imaging.jpeg.JpegMetadataReader;
|
---|
[4241] | 18 | import com.drew.imaging.jpeg.JpegProcessingException;
|
---|
[6209] | 19 | import com.drew.lang.Rational;
|
---|
[626] | 20 | import com.drew.metadata.Directory;
|
---|
| 21 | import com.drew.metadata.Metadata;
|
---|
[4241] | 22 | import com.drew.metadata.MetadataException;
|
---|
[626] | 23 | import com.drew.metadata.Tag;
|
---|
[9672] | 24 | import com.drew.metadata.exif.ExifDirectoryBase;
|
---|
[6127] | 25 | import com.drew.metadata.exif.ExifIFD0Directory;
|
---|
| 26 | import com.drew.metadata.exif.ExifSubIFDDirectory;
|
---|
[6209] | 27 | import com.drew.metadata.exif.GpsDirectory;
|
---|
[15219] | 28 | import com.drew.metadata.iptc.IptcDirectory;
|
---|
[626] | 29 |
|
---|
| 30 | /**
|
---|
[6209] | 31 | * Read out EXIF information from a JPEG file
|
---|
[626] | 32 | * @author Imi
|
---|
[6209] | 33 | * @since 99
|
---|
[626] | 34 | */
|
---|
[6362] | 35 | public final class ExifReader {
|
---|
[626] | 36 |
|
---|
[6360] | 37 | private ExifReader() {
|
---|
| 38 | // Hide default constructor for utils classes
|
---|
| 39 | }
|
---|
[6830] | 40 |
|
---|
[6209] | 41 | /**
|
---|
| 42 | * Returns the date/time from the given JPEG file.
|
---|
| 43 | * @param filename The JPEG file to read
|
---|
| 44 | * @return The date/time read in the EXIF section, or {@code null} if not found
|
---|
[17715] | 45 | * @deprecated Use {@link #readInstant(File)}
|
---|
[6209] | 46 | */
|
---|
[17715] | 47 | @Deprecated
|
---|
[9383] | 48 | public static Date readTime(File filename) {
|
---|
[17715] | 49 | Instant instant = readInstant(filename);
|
---|
| 50 | return instant == null ? null : Date.from(instant);
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | /**
|
---|
| 54 | * Returns the date/time from the given JPEG file.
|
---|
| 55 | * @param filename The JPEG file to read
|
---|
| 56 | * @return The date/time read in the EXIF section, or {@code null} if not found
|
---|
| 57 | */
|
---|
| 58 | public static Instant readInstant(File filename) {
|
---|
[1169] | 59 | try {
|
---|
[11745] | 60 | final Metadata metadata = JpegMetadataReader.readMetadata(filename);
|
---|
[17715] | 61 | return readInstant(metadata);
|
---|
[11745] | 62 | } catch (JpegProcessingException | IOException e) {
|
---|
[12620] | 63 | Logging.error(e);
|
---|
[11745] | 64 | }
|
---|
| 65 | return null;
|
---|
| 66 | }
|
---|
| 67 |
|
---|
| 68 | /**
|
---|
| 69 | * Returns the date/time from the given JPEG file.
|
---|
| 70 | * @param metadata The EXIF metadata
|
---|
| 71 | * @return The date/time read in the EXIF section, or {@code null} if not found
|
---|
| 72 | * @since 11745
|
---|
[17715] | 73 | * @deprecated Use {@link #readInstant(Metadata)}
|
---|
[11745] | 74 | */
|
---|
[17715] | 75 | @Deprecated
|
---|
[11745] | 76 | public static Date readTime(Metadata metadata) {
|
---|
[17715] | 77 | Instant instant = readInstant(metadata);
|
---|
| 78 | return instant == null ? null : Date.from(instant);
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | /**
|
---|
| 82 | * Returns the date/time from the given JPEG file.
|
---|
| 83 | * @param metadata The EXIF metadata
|
---|
| 84 | * @return The date/time read in the EXIF section, or {@code null} if not found
|
---|
| 85 | */
|
---|
| 86 | public static Instant readInstant(Metadata metadata) {
|
---|
[11745] | 87 | try {
|
---|
| 88 | String dateTimeOrig = null;
|
---|
[11514] | 89 | String dateTime = null;
|
---|
[11745] | 90 | String dateTimeDig = null;
|
---|
| 91 | String subSecOrig = null;
|
---|
| 92 | String subSec = null;
|
---|
| 93 | String subSecDig = null;
|
---|
| 94 | // The date fields are preferred in this order: DATETIME_ORIGINAL
|
---|
| 95 | // (0x9003), DATETIME (0x0132), DATETIME_DIGITIZED (0x9004). Some
|
---|
| 96 | // cameras store the fields in the wrong directory, so all
|
---|
| 97 | // directories are searched. Assume that the order of the fields
|
---|
| 98 | // in the directories is random.
|
---|
[6127] | 99 | for (Directory dirIt : metadata.getDirectories()) {
|
---|
[9648] | 100 | if (!(dirIt instanceof ExifDirectoryBase)) {
|
---|
| 101 | continue;
|
---|
| 102 | }
|
---|
[6127] | 103 | for (Tag tag : dirIt.getTags()) {
|
---|
[8244] | 104 | if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL /* 0x9003 */ &&
|
---|
| 105 | !tag.getDescription().matches("\\[[0-9]+ .+\\]")) {
|
---|
[11745] | 106 | dateTimeOrig = tag.getDescription();
|
---|
| 107 | } else if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */) {
|
---|
[11514] | 108 | dateTime = tag.getDescription();
|
---|
[11745] | 109 | } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */) {
|
---|
| 110 | dateTimeDig = tag.getDescription();
|
---|
| 111 | } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME_ORIGINAL /* 0x9291 */) {
|
---|
| 112 | subSecOrig = tag.getDescription();
|
---|
| 113 | } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME /* 0x9290 */) {
|
---|
| 114 | subSec = tag.getDescription();
|
---|
| 115 | } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME_DIGITIZED /* 0x9292 */) {
|
---|
| 116 | subSecDig = tag.getDescription();
|
---|
[4772] | 117 | }
|
---|
[1169] | 118 | }
|
---|
| 119 | }
|
---|
[11745] | 120 | String dateStr = null;
|
---|
| 121 | String subSeconds = null;
|
---|
| 122 | if (dateTimeOrig != null) {
|
---|
| 123 | // prefer TAG_DATETIME_ORIGINAL
|
---|
| 124 | dateStr = dateTimeOrig;
|
---|
| 125 | subSeconds = subSecOrig;
|
---|
| 126 | } else if (dateTime != null) {
|
---|
| 127 | // TAG_DATETIME is second choice, see #14209
|
---|
[11514] | 128 | dateStr = dateTime;
|
---|
[11745] | 129 | subSeconds = subSec;
|
---|
| 130 | } else if (dateTimeDig != null) {
|
---|
| 131 | dateStr = dateTimeDig;
|
---|
| 132 | subSeconds = subSecDig;
|
---|
[11514] | 133 | }
|
---|
[5610] | 134 | if (dateStr != null) {
|
---|
| 135 | dateStr = dateStr.replace('/', ':'); // workaround for HTC Sensation bug, see #7228
|
---|
[17715] | 136 | Instant date = DateUtils.parseInstant(dateStr);
|
---|
[9499] | 137 | if (subSeconds != null) {
|
---|
| 138 | try {
|
---|
[17715] | 139 | date = date.plusMillis((long) (TimeUnit.SECONDS.toMillis(1) * Double.parseDouble("0." + subSeconds)));
|
---|
[9499] | 140 | } catch (NumberFormatException e) {
|
---|
[12620] | 141 | Logging.warn("Failed parsing sub seconds from [{0}]", subSeconds);
|
---|
| 142 | Logging.warn(e);
|
---|
[9499] | 143 | }
|
---|
| 144 | }
|
---|
| 145 | return date;
|
---|
[5610] | 146 | }
|
---|
[14159] | 147 | } catch (UncheckedParseException | DateTimeException e) {
|
---|
[12620] | 148 | Logging.error(e);
|
---|
[626] | 149 | }
|
---|
[4772] | 150 | return null;
|
---|
[1169] | 151 | }
|
---|
[4241] | 152 |
|
---|
[6209] | 153 | /**
|
---|
| 154 | * Returns the image orientation of the given JPEG file.
|
---|
| 155 | * @param filename The JPEG file to read
|
---|
[6830] | 156 | * @return The image orientation as an {@code int}. Default value is 1. Possible values are listed in EXIF spec as follows:<br><ol>
|
---|
| 157 | * <li>The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.</li>
|
---|
| 158 | * <li>The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.</li>
|
---|
| 159 | * <li>The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.</li>
|
---|
| 160 | * <li>The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.</li>
|
---|
| 161 | * <li>The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.</li>
|
---|
| 162 | * <li>The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.</li>
|
---|
| 163 | * <li>The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.</li>
|
---|
| 164 | * <li>The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.</li></ol>
|
---|
[6209] | 165 | * @see <a href="http://www.impulseadventure.com/photo/exif-orientation.html">http://www.impulseadventure.com/photo/exif-orientation.html</a>
|
---|
[8509] | 166 | * @see <a href="http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto">
|
---|
| 167 | * http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto</a>
|
---|
[6209] | 168 | */
|
---|
| 169 | public static Integer readOrientation(File filename) {
|
---|
[4241] | 170 | try {
|
---|
| 171 | final Metadata metadata = JpegMetadataReader.readMetadata(filename);
|
---|
[8243] | 172 | final Directory dir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
|
---|
[9697] | 173 | return dir == null ? null : dir.getInteger(ExifIFD0Directory.TAG_ORIENTATION);
|
---|
| 174 | } catch (JpegProcessingException | IOException e) {
|
---|
[12620] | 175 | Logging.error(e);
|
---|
[4241] | 176 | }
|
---|
[6209] | 177 | return null;
|
---|
[4241] | 178 | }
|
---|
| 179 |
|
---|
[6209] | 180 | /**
|
---|
| 181 | * Returns the geolocation of the given JPEG file.
|
---|
| 182 | * @param filename The JPEG file to read
|
---|
| 183 | * @return The lat/lon read in the EXIF section, or {@code null} if not found
|
---|
| 184 | * @since 6209
|
---|
| 185 | */
|
---|
| 186 | public static LatLon readLatLon(File filename) {
|
---|
| 187 | try {
|
---|
| 188 | final Metadata metadata = JpegMetadataReader.readMetadata(filename);
|
---|
[8243] | 189 | final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
|
---|
[6209] | 190 | return readLatLon(dirGps);
|
---|
[9697] | 191 | } catch (JpegProcessingException | IOException | MetadataException e) {
|
---|
[12620] | 192 | Logging.error(e);
|
---|
[6209] | 193 | }
|
---|
| 194 | return null;
|
---|
| 195 | }
|
---|
| 196 |
|
---|
| 197 | /**
|
---|
| 198 | * Returns the geolocation of the given EXIF GPS directory.
|
---|
| 199 | * @param dirGps The EXIF GPS directory
|
---|
| 200 | * @return The lat/lon read in the EXIF section, or {@code null} if {@code dirGps} is null
|
---|
[8470] | 201 | * @throws MetadataException if invalid metadata is given
|
---|
[6209] | 202 | * @since 6209
|
---|
| 203 | */
|
---|
| 204 | public static LatLon readLatLon(GpsDirectory dirGps) throws MetadataException {
|
---|
[17548] | 205 | if (dirGps != null && dirGps.getTagCount() > 1) {
|
---|
[8132] | 206 | double lat = readAxis(dirGps, GpsDirectory.TAG_LATITUDE, GpsDirectory.TAG_LATITUDE_REF, 'S');
|
---|
| 207 | double lon = readAxis(dirGps, GpsDirectory.TAG_LONGITUDE, GpsDirectory.TAG_LONGITUDE_REF, 'W');
|
---|
[6209] | 208 | return new LatLon(lat, lon);
|
---|
| 209 | }
|
---|
| 210 | return null;
|
---|
| 211 | }
|
---|
[6830] | 212 |
|
---|
[6209] | 213 | /**
|
---|
| 214 | * Returns the direction of the given JPEG file.
|
---|
| 215 | * @param filename The JPEG file to read
|
---|
[8509] | 216 | * @return The direction of the image when it was captures (in degrees between 0.0 and 359.99),
|
---|
[11745] | 217 | * or {@code null} if not found
|
---|
[6209] | 218 | * @since 6209
|
---|
| 219 | */
|
---|
| 220 | public static Double readDirection(File filename) {
|
---|
| 221 | try {
|
---|
| 222 | final Metadata metadata = JpegMetadataReader.readMetadata(filename);
|
---|
[8243] | 223 | final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
|
---|
[6209] | 224 | return readDirection(dirGps);
|
---|
[9697] | 225 | } catch (JpegProcessingException | IOException e) {
|
---|
[12620] | 226 | Logging.error(e);
|
---|
[6209] | 227 | }
|
---|
| 228 | return null;
|
---|
| 229 | }
|
---|
[6830] | 230 |
|
---|
[6209] | 231 | /**
|
---|
| 232 | * Returns the direction of the given EXIF GPS directory.
|
---|
| 233 | * @param dirGps The EXIF GPS directory
|
---|
[11745] | 234 | * @return The direction of the image when it was captured (in degrees between 0.0 and 359.99),
|
---|
[8509] | 235 | * or {@code null} if missing or if {@code dirGps} is null
|
---|
[6209] | 236 | * @since 6209
|
---|
| 237 | */
|
---|
| 238 | public static Double readDirection(GpsDirectory dirGps) {
|
---|
| 239 | if (dirGps != null) {
|
---|
[8132] | 240 | Rational direction = dirGps.getRational(GpsDirectory.TAG_IMG_DIRECTION);
|
---|
[6209] | 241 | if (direction != null) {
|
---|
| 242 | return direction.doubleValue();
|
---|
| 243 | }
|
---|
| 244 | }
|
---|
| 245 | return null;
|
---|
| 246 | }
|
---|
| 247 |
|
---|
[10378] | 248 | private static double readAxis(GpsDirectory dirGps, int gpsTag, int gpsTagRef, char cRef) throws MetadataException {
|
---|
[6209] | 249 | double value;
|
---|
| 250 | Rational[] components = dirGps.getRationalArray(gpsTag);
|
---|
| 251 | if (components != null) {
|
---|
| 252 | double deg = components[0].doubleValue();
|
---|
| 253 | double min = components[1].doubleValue();
|
---|
| 254 | double sec = components[2].doubleValue();
|
---|
[6830] | 255 |
|
---|
[6209] | 256 | if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
|
---|
[7864] | 257 | throw new IllegalArgumentException("deg, min and sec are NaN");
|
---|
[6830] | 258 |
|
---|
[9970] | 259 | value = Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600));
|
---|
[6830] | 260 |
|
---|
[12985] | 261 | String s = dirGps.getString(gpsTagRef);
|
---|
| 262 | if (s != null && s.charAt(0) == cRef) {
|
---|
[6209] | 263 | value = -value;
|
---|
| 264 | }
|
---|
| 265 | } else {
|
---|
| 266 | // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)
|
---|
| 267 | value = dirGps.getDouble(gpsTag);
|
---|
| 268 | }
|
---|
| 269 | return value;
|
---|
| 270 | }
|
---|
[7956] | 271 |
|
---|
| 272 | /**
|
---|
[11745] | 273 | * Returns the speed of the given JPEG file.
|
---|
| 274 | * @param filename The JPEG file to read
|
---|
| 275 | * @return The speed of the camera when the image was captured (in km/h),
|
---|
| 276 | * or {@code null} if not found
|
---|
| 277 | * @since 11745
|
---|
| 278 | */
|
---|
| 279 | public static Double readSpeed(File filename) {
|
---|
| 280 | try {
|
---|
| 281 | final Metadata metadata = JpegMetadataReader.readMetadata(filename);
|
---|
| 282 | final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
|
---|
| 283 | return readSpeed(dirGps);
|
---|
| 284 | } catch (JpegProcessingException | IOException e) {
|
---|
[12620] | 285 | Logging.error(e);
|
---|
[11745] | 286 | }
|
---|
| 287 | return null;
|
---|
| 288 | }
|
---|
| 289 |
|
---|
| 290 | /**
|
---|
| 291 | * Returns the speed of the given EXIF GPS directory.
|
---|
| 292 | * @param dirGps The EXIF GPS directory
|
---|
| 293 | * @return The speed of the camera when the image was captured (in km/h),
|
---|
| 294 | * or {@code null} if missing or if {@code dirGps} is null
|
---|
| 295 | * @since 11745
|
---|
| 296 | */
|
---|
| 297 | public static Double readSpeed(GpsDirectory dirGps) {
|
---|
| 298 | if (dirGps != null) {
|
---|
| 299 | Double speed = dirGps.getDoubleObject(GpsDirectory.TAG_SPEED);
|
---|
| 300 | if (speed != null) {
|
---|
| 301 | final String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
|
---|
| 302 | if ("M".equalsIgnoreCase(speedRef)) {
|
---|
| 303 | // miles per hour
|
---|
| 304 | speed *= SystemOfMeasurement.IMPERIAL.bValue / 1000;
|
---|
| 305 | } else if ("N".equalsIgnoreCase(speedRef)) {
|
---|
| 306 | // knots == nautical miles per hour
|
---|
| 307 | speed *= SystemOfMeasurement.NAUTICAL_MILE.bValue / 1000;
|
---|
| 308 | }
|
---|
| 309 | // default is K (km/h)
|
---|
| 310 | return speed;
|
---|
| 311 | }
|
---|
| 312 | }
|
---|
| 313 | return null;
|
---|
| 314 | }
|
---|
| 315 |
|
---|
| 316 | /**
|
---|
| 317 | * Returns the elevation of the given JPEG file.
|
---|
| 318 | * @param filename The JPEG file to read
|
---|
| 319 | * @return The elevation of the camera when the image was captured (in m),
|
---|
| 320 | * or {@code null} if not found
|
---|
| 321 | * @since 11745
|
---|
| 322 | */
|
---|
| 323 | public static Double readElevation(File filename) {
|
---|
| 324 | try {
|
---|
| 325 | final Metadata metadata = JpegMetadataReader.readMetadata(filename);
|
---|
| 326 | final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
|
---|
| 327 | return readElevation(dirGps);
|
---|
| 328 | } catch (JpegProcessingException | IOException e) {
|
---|
[12620] | 329 | Logging.error(e);
|
---|
[11745] | 330 | }
|
---|
| 331 | return null;
|
---|
| 332 | }
|
---|
| 333 |
|
---|
| 334 | /**
|
---|
| 335 | * Returns the elevation of the given EXIF GPS directory.
|
---|
| 336 | * @param dirGps The EXIF GPS directory
|
---|
| 337 | * @return The elevation of the camera when the image was captured (in m),
|
---|
| 338 | * or {@code null} if missing or if {@code dirGps} is null
|
---|
| 339 | * @since 11745
|
---|
| 340 | */
|
---|
| 341 | public static Double readElevation(GpsDirectory dirGps) {
|
---|
| 342 | if (dirGps != null) {
|
---|
| 343 | Double ele = dirGps.getDoubleObject(GpsDirectory.TAG_ALTITUDE);
|
---|
| 344 | if (ele != null) {
|
---|
| 345 | final Integer d = dirGps.getInteger(GpsDirectory.TAG_ALTITUDE_REF);
|
---|
| 346 | if (d != null && d.intValue() == 1) {
|
---|
| 347 | ele *= -1;
|
---|
| 348 | }
|
---|
| 349 | return ele;
|
---|
| 350 | }
|
---|
| 351 | }
|
---|
| 352 | return null;
|
---|
| 353 | }
|
---|
| 354 |
|
---|
| 355 | /**
|
---|
[15219] | 356 | * Returns the caption of the given IPTC directory.
|
---|
| 357 | * @param dirIptc The IPTC directory
|
---|
| 358 | * @return The caption entered, or {@code null} if missing or if {@code dirIptc} is null
|
---|
| 359 | * @since 15219
|
---|
| 360 | */
|
---|
| 361 | public static String readCaption(IptcDirectory dirIptc) {
|
---|
| 362 | return dirIptc == null ? null : dirIptc.getDescription(IptcDirectory.TAG_CAPTION);
|
---|
| 363 | }
|
---|
| 364 |
|
---|
| 365 | /**
|
---|
| 366 | * Returns the headline of the given IPTC directory.
|
---|
| 367 | * @param dirIptc The IPTC directory
|
---|
| 368 | * @return The headline entered, or {@code null} if missing or if {@code dirIptc} is null
|
---|
| 369 | * @since 15219
|
---|
| 370 | */
|
---|
| 371 | public static String readHeadline(IptcDirectory dirIptc) {
|
---|
| 372 | return dirIptc == null ? null : dirIptc.getDescription(IptcDirectory.TAG_HEADLINE);
|
---|
| 373 | }
|
---|
| 374 |
|
---|
| 375 | /**
|
---|
| 376 | * Returns the keywords of the given IPTC directory.
|
---|
| 377 | * @param dirIptc The IPTC directory
|
---|
| 378 | * @return The keywords entered, or {@code null} if missing or if {@code dirIptc} is null
|
---|
| 379 | * @since 15219
|
---|
| 380 | */
|
---|
| 381 | public static List<String> readKeywords(IptcDirectory dirIptc) {
|
---|
| 382 | return dirIptc == null ? null : dirIptc.getKeywords();
|
---|
| 383 | }
|
---|
| 384 |
|
---|
| 385 | /**
|
---|
| 386 | * Returns the object name of the given IPTC directory.
|
---|
| 387 | * @param dirIptc The IPTC directory
|
---|
| 388 | * @return The object name entered, or {@code null} if missing or if {@code dirIptc} is null
|
---|
| 389 | * @since 15219
|
---|
| 390 | */
|
---|
| 391 | public static String readObjectName(IptcDirectory dirIptc) {
|
---|
| 392 | return dirIptc == null ? null : dirIptc.getDescription(IptcDirectory.TAG_OBJECT_NAME);
|
---|
| 393 | }
|
---|
| 394 |
|
---|
| 395 | /**
|
---|
[7956] | 396 | * Returns a Transform that fixes the image orientation.
|
---|
| 397 | *
|
---|
[10378] | 398 | * Only orientation 1, 3, 6 and 8 are supported. Everything else is treated as 1.
|
---|
[7956] | 399 | * @param orientation the exif-orientation of the image
|
---|
| 400 | * @param width the original width of the image
|
---|
| 401 | * @param height the original height of the image
|
---|
| 402 | * @return a transform that rotates the image, so it is upright
|
---|
| 403 | */
|
---|
| 404 | public static AffineTransform getRestoreOrientationTransform(final int orientation, final int width, final int height) {
|
---|
| 405 | final int q;
|
---|
| 406 | final double ax, ay;
|
---|
| 407 | switch (orientation) {
|
---|
| 408 | case 8:
|
---|
| 409 | q = -1;
|
---|
[8364] | 410 | ax = width / 2d;
|
---|
| 411 | ay = width / 2d;
|
---|
[7956] | 412 | break;
|
---|
| 413 | case 3:
|
---|
| 414 | q = 2;
|
---|
[8364] | 415 | ax = width / 2d;
|
---|
| 416 | ay = height / 2d;
|
---|
[7956] | 417 | break;
|
---|
| 418 | case 6:
|
---|
| 419 | q = 1;
|
---|
[8364] | 420 | ax = height / 2d;
|
---|
| 421 | ay = height / 2d;
|
---|
[7956] | 422 | break;
|
---|
| 423 | default:
|
---|
| 424 | q = 0;
|
---|
| 425 | ax = 0;
|
---|
| 426 | ay = 0;
|
---|
| 427 | }
|
---|
| 428 | return AffineTransform.getQuadrantRotateInstance(q, ax, ay);
|
---|
| 429 | }
|
---|
| 430 |
|
---|
| 431 | /**
|
---|
| 432 | * Check, if the given orientation switches width and height of the image.
|
---|
| 433 | * E.g. 90 degree rotation
|
---|
| 434 | *
|
---|
| 435 | * Only orientation 1, 3, 6 and 8 are supported. Everything else is treated
|
---|
| 436 | * as 1.
|
---|
| 437 | * @param orientation the exif-orientation of the image
|
---|
| 438 | * @return true, if it switches width and height
|
---|
| 439 | */
|
---|
| 440 | public static boolean orientationSwitchesDimensions(int orientation) {
|
---|
| 441 | return orientation == 6 || orientation == 8;
|
---|
| 442 | }
|
---|
| 443 |
|
---|
| 444 | /**
|
---|
| 445 | * Check, if the given orientation requires any correction to the image.
|
---|
| 446 | *
|
---|
| 447 | * Only orientation 1, 3, 6 and 8 are supported. Everything else is treated
|
---|
| 448 | * as 1.
|
---|
| 449 | * @param orientation the exif-orientation of the image
|
---|
| 450 | * @return true, unless the orientation value is 1 or unsupported.
|
---|
| 451 | */
|
---|
| 452 | public static boolean orientationNeedsCorrection(int orientation) {
|
---|
| 453 | return orientation == 3 || orientation == 6 || orientation == 8;
|
---|
| 454 | }
|
---|
[626] | 455 | }
|
---|