Skip to content

Commit

Permalink
Support GeoTIFF data in TIFF
Browse files Browse the repository at this point in the history
  • Loading branch information
don-vip committed Jun 19, 2022
1 parent 32cfb4c commit 1090bd1
Show file tree
Hide file tree
Showing 4 changed files with 2,883 additions and 1 deletion.
34 changes: 33 additions & 1 deletion Source/com/drew/metadata/exif/ExifDirectoryBase.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
* Copyright 2002-2022 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -137,6 +137,13 @@ public abstract class ExifDirectoryBase extends Directory
public static final int TAG_WHITE_POINT = 0x013E;
public static final int TAG_PRIMARY_CHROMATICITIES = 0x013F;

/**
* A color map for palette color images.
* This field defines a Red-Green-Blue color map (often called a lookup table) for palette-color images.
* In a palette-color image, a pixel value is used to index into an RGB lookup table.
*/
public static final int TAG_COLOR_MAP = 0x0140;

public static final int TAG_TILE_WIDTH = 0x0142;
public static final int TAG_TILE_LENGTH = 0x0143;
public static final int TAG_TILE_OFFSETS = 0x0144;
Expand Down Expand Up @@ -190,9 +197,22 @@ public abstract class ExifDirectoryBase extends Directory
* The actual F-number(F-stop) of lens when the image was taken.
*/
public static final int TAG_FNUMBER = 0x829D;

public static final int TAG_PIXEL_SCALE = 0x830E;

public static final int TAG_IPTC_NAA = 0x83BB;

public static final int TAG_MODEL_TIE_POINT = 0x8482;

public static final int TAG_PHOTOSHOP_SETTINGS = 0x8649;
public static final int TAG_INTER_COLOR_PROFILE = 0x8773;

public static final int TAG_GEOTIFF_GEO_KEYS = 0x87AF;

public static final int TAG_GEOTIFF_GEO_DOUBLE_PARAMS = 0x87B0;

public static final int TAG_GEOTIFF_GEO_ASCII_PARAMS = 0x87B1;

/**
* Exposure program that the camera used when image was taken. '1' means
* manual control, '2' program normal, '3' aperture priority, '4' shutter
Expand Down Expand Up @@ -601,6 +621,10 @@ public abstract class ExifDirectoryBase extends Directory
public static final int TAG_LENS_MODEL = 0xA434;
/** String. */
public static final int TAG_LENS_SERIAL_NUMBER = 0xA435;

public static final int TAG_GDAL_METADATA = 0xA480;
public static final int TAG_GDAL_NO_DATA = 0xA481;

/** Rational64u. */
public static final int TAG_GAMMA = 0xA500;

Expand Down Expand Up @@ -651,6 +675,7 @@ protected static void addExifTagNames(HashMap<Integer, String> map)
map.put(TAG_HOST_COMPUTER, "Host Computer");
map.put(TAG_WHITE_POINT, "White Point");
map.put(TAG_PRIMARY_CHROMATICITIES, "Primary Chromaticities");
map.put(TAG_COLOR_MAP, "Color Map");
map.put(TAG_TILE_WIDTH, "Tile Width");
map.put(TAG_TILE_LENGTH, "Tile Length");
map.put(TAG_TILE_OFFSETS, "Tile Offsets");
Expand Down Expand Up @@ -685,9 +710,14 @@ protected static void addExifTagNames(HashMap<Integer, String> map)
map.put(TAG_COPYRIGHT, "Copyright");
map.put(TAG_EXPOSURE_TIME, "Exposure Time");
map.put(TAG_FNUMBER, "F-Number");
map.put(TAG_PIXEL_SCALE, "Pixel Scale");
map.put(TAG_IPTC_NAA, "IPTC/NAA");
map.put(TAG_MODEL_TIE_POINT, "Model Tie Point");
map.put(TAG_PHOTOSHOP_SETTINGS, "Photoshop Settings");
map.put(TAG_INTER_COLOR_PROFILE, "Inter Color Profile");
map.put(TAG_GEOTIFF_GEO_KEYS, "GeoTIFF Geo Keys");
map.put(TAG_GEOTIFF_GEO_DOUBLE_PARAMS, "GeoTIFF Geo Double Params");
map.put(TAG_GEOTIFF_GEO_ASCII_PARAMS, "GeoTIFF Geo ASCII Params");
map.put(TAG_EXPOSURE_PROGRAM, "Exposure Program");
map.put(TAG_SPECTRAL_SENSITIVITY, "Spectral Sensitivity");
map.put(TAG_ISO_EQUIVALENT, "ISO Speed Ratings");
Expand Down Expand Up @@ -781,6 +811,8 @@ protected static void addExifTagNames(HashMap<Integer, String> map)
map.put(TAG_LENS_MAKE, "Lens Make");
map.put(TAG_LENS_MODEL, "Lens Model");
map.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
map.put(TAG_GDAL_METADATA, "GDAL Metadata");
map.put(TAG_GDAL_NO_DATA, "GDAL NoData");
map.put(TAG_GAMMA, "Gamma");
map.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
map.put(TAG_PANASONIC_TITLE, "Panasonic Title");
Expand Down
100 changes: 100 additions & 0 deletions Source/com/drew/metadata/exif/ExifTiffHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
Expand All @@ -42,6 +44,7 @@
import com.drew.metadata.StringValue;
import com.drew.metadata.apple.AppleRunTimeReader;
import com.drew.metadata.exif.makernotes.*;
import com.drew.metadata.geotiff.GeoTiffDirectory;
import com.drew.metadata.icc.IccReader;
import com.drew.metadata.iptc.IptcReader;
import com.drew.metadata.photoshop.PhotoshopReader;
Expand Down Expand Up @@ -172,6 +175,103 @@ public boolean hasFollowerIfd()
return false;
}

@Override
public void endingIFD(TiffReaderContext context)
{
if (_currentDirectory instanceof ExifIFD0Directory)
{
ExifIFD0Directory directory = (ExifIFD0Directory) _currentDirectory;
int[] geoKeys = directory.getIntArray(ExifDirectoryBase.TAG_GEOTIFF_GEO_KEYS);
if (geoKeys != null)
{
// GetTIFF stores data in its own format within TIFF. It is TIFF-like, but different.
// It can reference data from tags that have not been visited yet, so we must unpack it
// once the directory is complete.
processGeoTiff(geoKeys, directory);
}
}

super.endingIFD(context);
}

private void processGeoTiff(int[] geoKeys, ExifIFD0Directory sourceDirectory)
{
if (geoKeys.length < 4)
return;

pushDirectory(GeoTiffDirectory.class);

int i = 0;

int directoryVersion = geoKeys[i++];
int revision = geoKeys[i++];
int minorRevision = geoKeys[i++];
int numberOfKeys = geoKeys[i++];

// TODO store these values in negative tag IDs

Set<Integer> sourceTags = new HashSet<Integer>(ExifDirectoryBase.TAG_GEOTIFF_GEO_KEYS);

for (int j = 0; j < numberOfKeys; j++)
{
int keyId = geoKeys[i++];
int tiffTagLocation = geoKeys[i++];
int valueCount = geoKeys[i++];
int valueOffset = geoKeys[i++];

if (tiffTagLocation == 0)
{
// Identifies the tag containing the value. If zero, then the value is ushort and stored
// in valueOffset directly, and the value count is implied as 1.
_currentDirectory.setInt(keyId, valueOffset);
}
else
{
// The value is stored in another tag.
int sourceTagId = tiffTagLocation;
sourceTags.add(sourceTagId);
Object sourceValue = sourceDirectory.getObject(sourceTagId);
if (sourceValue instanceof StringValue)
{
StringValue sourceString = (StringValue) sourceValue;
int sourceStringBytesLength = sourceString.getBytes().length;
if (valueOffset + valueCount <= sourceStringBytesLength)
{
// ASCII values appear to have a | character and the end, so we trim it off here
_currentDirectory.setString(keyId, sourceString.toString(valueOffset, valueCount).replaceAll("\\|$", ""));
}
else
{
_currentDirectory.addError("GeoTIFF key "+keyId+" with offset "+valueOffset+" and count "+valueCount+" extends beyond length of source value ("+sourceStringBytesLength+")");
}
}
else if (sourceValue instanceof double[])
{
double[] sourceArray = (double[]) sourceValue;
if (valueOffset + valueCount <= sourceArray.length)
{
double[] array = new double[valueCount];
System.arraycopy(sourceArray, valueOffset, array, 0, valueCount);
_currentDirectory.setDoubleArray(keyId, array);
}
else
{
_currentDirectory.addError("GeoTIFF key "+keyId+" with offset "+valueOffset+" and count "+valueCount+" extends beyond length of source value ("+sourceArray.length+")");
}
}
else
{
_currentDirectory.addError("GeoTIFF key "+keyId+" references tag "+sourceTagId+" which has unsupported type of "+sourceValue.getClass());
}
}
}

for (Integer sourceTag : sourceTags)
{
sourceDirectory.removeTag(sourceTag);
}
}

@Override
@Nullable
public Long tryCustomProcessFormat(final int tagId, final int formatCode, final long componentCount)
Expand Down
Loading

0 comments on commit 1090bd1

Please sign in to comment.