|
Here is a sample for modifiying EXIF data that lives in a JPG file. This will not work on TIFFs (ImgSource currently has no way to modify EXIF in TIFF, and there is no timeline that says when it will). This only applies to ImgSource 3.0.44.0 and higher.
HISSRC hSrc = IS3OpenFileSource(sourceJPGFile);
force a JPG marker read
UINT32 w, h, d;
IS3GetJPGDims(hSrc, &w, &h, &d, NULL);
// copy all JPEG_APPx markes from the source to the ImgSource
// JPG output marker array
IS3CopyJPGInputMetadataToOutputMetadata();
// loop over all input JPEG_APPx markers
int uMarkerCount = IS3GetJPGInputMarkerCount();
for (int uMarkerIdx=0; uMarkerIdx < uMarkerCount; uMarkerIdx++)
{
// peek at this marker...
UINT32 uMarkerLen = 0;
UINT32 uMarkerType = 0;
IS3GetJPGInputMarkerInfo(uMarkerIdx, &uMarkerLen, &uMarkerType);
// need JPEG_APP1 (0xe1)
if (uMarkerType == 0xe1)
{
bool bModifiedData = false;
// get the marker data
HGLOBAL hMarkerData = NULL;
IS3GetJPGInputMarker(uMarkerIdx, &hMarkerData, &uMarkerLen, &uMarkerType);
BYTE *pMarkerData = (BYTE *)hMarkerData;
if (pMarkerData)
{
// attempt to parse it for EXIF data
HANDLE hEXIF = IS3EXIFInitializeFromByteArray(pMarkerData, uMarkerLen, 1);
if (hEXIF)
{
__int32 iTagPos = 0;
__int32 iIFDPos = 0;
UINT32 uOutFlags = 0;
// 274 = orient, 0x9003 = DateTimeOriginal, etc..
UINT32 uTagID = 0x9003;
BOOL bFoundTag = IS3EXIFGetTagPos(hEXIF, uTagID, 63, &iTagPos, &iIFDPos, &uOutFlags);
// done with the parser
IS3EXIFRelease(hEXIF);
// don't even bother looking at EXIF from TIFF
bool bIsTiffTag = ((uOutFlags & 0x01) != 0);
// this will be important, later on
bool bIsMotorolaOrder = ((uOutFlags & 0x02) != 0);
if (bFoundTag && (iTagPos > 0) && (!bIsTiffTag))
{
/*--------------------------------------------
If the tag is stored in Motorola byte order,
all members of this structure must be byte-swapped
before you can read them correctly.
--------------------------------------------
typedef struct
{
UINT16 tdir_tag;
UINT16 tdir_type;
UINT32 tdir_count;
UINT32 tdir_offset;
} ISTIFFDirEntry;
/*--------------------------------------------
Tag Type Values
tag type ID Format Byte-swap
TIFF_BYTE = 1, 8-bit unsigned integer
TIFF_ASCII = 2, 8-bit bytes w/ last byte null
TIFF_SHORT = 3, 16-bit unsigned integer *
TIFF_LONG = 4, 32-bit unsigned integer *
TIFF_RATIONAL = 5, 64-bit unsigned fraction &
TIFF_SBYTE = 6, 8-bit signed integer
TIFF_UNDEFINED = 7, 8-bit untyped data
TIFF_SSHORT = 8, 16-bit signed integer *
TIFF_SLONG = 9, 32-bit signed integer *
TIFF_SRATIONAL = 10, 64-bit signed fraction &
TIFF_FLOAT = 11, 32-bit IEEE floating point
TIFF_DOUBLE = 12, 64-bit IEEE floating point
* = requires byte swap
& = requires byte swap on both parts
--------------------------------------------
// sizes of the types above. first is 0 because there's no 0 tag type.
int typeSize[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
/*--------------------------------------------
now that we know the tag exists and where it lives,
we can look at it. well, almost. first we need to
do some byte-swapping in case the data is encoded in
Motorola byte order. and, to help avoid confusion, we'll
make a copy of that data and work on the copy.
--------------------------------------------
ISTIFFDirEntry *pOriginalTDE = (ISTIFFDirEntry *)(pMarkerData + iTagPos);
ISTIFFDirEntry tde;
memcpy(&tde, pOriginalTDE, sizeof(ISTIFFDirEntry));
if (bIsMotorolaOrder)
{
// swap all the data
IS3SwapShort(&tde.tdir_tag);
IS3SwapShort(&tde.tdir_type);
IS3SwapLong(&tde.tdir_count);
IS3SwapLong(&tde.tdir_offset);
}
/*--------------------------------------------
now we can look at the tde structure and learn about
what the tag holds.
first, the tag ID... that should match the tag we requested.
if it doesn't, there's something seriously wrong with ImgSource.
--------------------------------------------
if (uTagID == tde.tdir_tag)
{
/*--------------------------------------------
now we need to locate the actual tag data.
note that the tde struct doesn't have a "data"
member. that's because tag data is usually stored at some
memory location offset from the start of the IFD - usually.
if the amount of data is four bytes or smaller, however,
the data is stored in the tdir_offset member itself.
so, we need to calculate the amount of data the tag
refers to before we can know where the actual data lives.
this is calculated by multiplying the tag data count by
the size of the tag data type.
--------------------------------------------
UINT32 uDataSize = typeSize[tde.tdir_type] * tde.tdir_count;
BYTE *pTagData = NULL;
if (uDataSize > 4)
{
// tag data is stored at this location:
pTagData = pMarkerData + (tde.tdir_offset) + iIFDPos;
}
else
{
// tag data is stored in the tdir_offset itself
pTagData = (BYTE *)(&pOriginalTDE->tdir_offset);
}
/*--------------------------------------------
we know where the data lives, so now we can modify the
tde.tdir_count bytes starting at pTagData.
Note: DO NOT change more than uDataSize bytes, or you
will run into tag data for other tags (or into other tags,
if the data in in the tdir_offset member).
Note: DO NOT attempt to change the size of any tag data.
even if you are careful to update the offsets for all tags
in an IFD (and any IFDs that follow it in the EXIF data block),
you will most likely destroy any MakerNote information
stored in the EXIF block because various camera manufacturers
use unusual schemes to calculate the data offsets for tags
stored in MakerNotes. without knowing the specific MakerNote
format used by the camera, you will likely break all the tag
offsets.
before modifing anything, though, we need to be aware of
byte-order issues, the same as we did when we looked at the
tag structure: if the tag is in motorola byte order, we
need to do byte-swapping on the data types that require
it (16 and 32 bit integers and the 'rational' types).
ex. if we're setting 8 bit data (8-bit ints, TIFF_ASCII, or
the TIFF_UNDEFINED types), we simply set the bytes we want:
// change the year in tag 0x9003 (DateTimeOriginal) to '1998'
pTagData[0] = '1';
pTagData[1] = '9';
pTagData[2] = '9';
pTagData[3] = '8';
ex. set some TIFF_BYTE data:
BYTE data[7] = {8,6,7,5,3,0,9};
memcpy(pTagData, data, 7);
ex. set some 16-bit integers
UINT16 data1 = 1938;
UINT16 data2 = 1945;
if (bIsMotorolaOrder)
{
IS3SwapShort(&data1);
IS3SwapShort(&data2);
}
memcpy(pTagData, &data1, 2);
memcpy(pTagData + 2, &data2, 2);
ex. set some 32-bit integers
UINT32 data1 = 11000;
UINT32 data2 = 3000;
if (bIsMotorolaOrder)
{
IS3SwapLong(&data1);
IS3SwapLong(&data2);
}
memcpy(pTagData, &data1, 4);
memcpy(pTagData + 4, &data2, 4);
ex. set a TIFF_RATIONAL value
UINT32 numerator = 55;
UINT32 denominator = 75;
if (bIsMotorolaOrder)
{
IS3SwapLong(&numerator);
IS3SwapLong(&denominator);
}
memcpy(pTagData, &numerator, 4);
memcpy(pTagData + 4, &denominator, 4);
ex. set a TIFF_DOUBLE value
double val = 1.21;
memcpy(pTagData, &val, 8);
etc.
--------------------------------------------
// ... your code goes here ...
pTagData[0] = '1';
pTagData[1] = '9';
pTagData[2] = '9';
pTagData[3] = '8';
// did you modify anything?
bModifiedData = true;
} // tag received = tag requested
} // found the tag, it's not a TIFF tag, and the position is OK
} // EXIF OK
/*--------------------------------------------
set that modified data block into ImgSource's output
JPEG markers array.
note that at the very beginning of this, we copied all
input markers to the output marker array using
IS3CopyJPGInputMetadataToOutputMetadata(). so now, we're
just updating the markers we actually changed.
when we go to write the new file, it will contain all
JPEG_APPx markers: those we didn't change, and those we did.
--------------------------------------------
if (bModifiedData)
{
IS3SetJPGOutputMarker(uMarkerIdx, pMarkerData + 4, uMarkerLen - 4, 0xe1);
}
// OK to free. the call above makes a copy of the data.
GlobalFree(hMarkerData);
} // marker not NULL
} // APP1 marker test
} // marker loop
/*--------------------------------------------
OK. so that was all very exciting.
at this point we have a bunch of data blocks
sitting in ImgSource's JPG output marker array, ready
to be written to an output JPG file. most importantly,
we have a set of markers that are nearly identical
to the markers in the original - they are identical
in order, in type, in size, and on the whole, in
content. the only difference is that you might have
changed the values of a few bytes in a few places in
one (usually, though possibly multiple) JPEG_APP1 marker.
now, we'll give these markers to IS3CopyJPGWithNewMarkers
which will copy the source file into a new file, replacing
each JPEG_APPx markers it finds with a marker from
ImgSource's JPEG output marker array. and, specifically,
it will replace inMarker[i] with outMarker[i] - that's why
it's important that order and type match.
--------------------------------------------
// rewind the source
IS3Seek(hSrc,0,0);
// open output
HISDEST hDest = IS3OpenFileDest(TEMP"pix/foo.jpg", 0);
/*--------------------------------------------
the 1 << 6 here sets bit #6 in the flags.
that tells the function to do a 'replace mode'
copy. this mode preserves marker ordering and
positioning. this is important to JPEG/EXIF files,
because some readers (especially cameras) are
sensitive to the position of the markers.
--------------------------------------------
IS3CopyJPGWithNewMarkers(hSrc, hDest, 1 << 6);
// done!!!
IS3CloseDest(hDest);
IS3CloseSource(hSrc);
Copyright © 2013, Smaller Animals Software, Inc. |
Site Stuff
Smaller Animals NewsImgSource
ThumbNailer 10
|