| /*****************************************************************************/ |
| // Copyright 2006-2008 Adobe Systems Incorporated |
| // All Rights Reserved. |
| // |
| // NOTICE: Adobe permits you to use, modify, and distribute this file in |
| // accordance with the terms of the Adobe license agreement accompanying it. |
| /*****************************************************************************/ |
| |
| /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_iptc.cpp#1 $ */ |
| /* $DateTime: 2012/05/30 13:28:51 $ */ |
| /* $Change: 832332 $ */ |
| /* $Author: tknoll $ */ |
| |
| /*****************************************************************************/ |
| |
| #include "dng_iptc.h" |
| |
| #include "dng_assertions.h" |
| #include "dng_auto_ptr.h" |
| #include "dng_memory_stream.h" |
| #include "dng_stream.h" |
| #include "dng_utils.h" |
| |
| /*****************************************************************************/ |
| |
| dng_iptc::dng_iptc () |
| |
| : fTitle () |
| |
| , fUrgency (-1) |
| |
| , fCategory () |
| |
| , fSupplementalCategories () |
| |
| , fKeywords () |
| |
| , fInstructions () |
| |
| , fDateTimeCreated () |
| |
| , fDigitalCreationDateTime () |
| |
| , fAuthors () |
| , fAuthorsPosition () |
| |
| , fCity () |
| , fState () |
| , fCountry () |
| , fCountryCode () |
| |
| , fLocation () |
| |
| , fTransmissionReference () |
| |
| , fHeadline () |
| |
| , fCredit () |
| |
| , fSource () |
| |
| , fCopyrightNotice () |
| |
| , fDescription () |
| , fDescriptionWriter () |
| |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_iptc::~dng_iptc () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_iptc::IsEmpty () const |
| { |
| |
| if (fTitle.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fUrgency >= 0) |
| { |
| return false; |
| } |
| |
| if (fCategory.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fSupplementalCategories.Count () > 0) |
| { |
| return false; |
| } |
| |
| if (fKeywords.Count () > 0) |
| { |
| return false; |
| } |
| |
| if (fInstructions.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fDateTimeCreated.IsValid ()) |
| { |
| return false; |
| } |
| |
| if (fDigitalCreationDateTime.IsValid ()) |
| { |
| return false; |
| } |
| |
| if (fAuthors.Count () != 0 || |
| fAuthorsPosition.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fCity .NotEmpty () || |
| fState .NotEmpty () || |
| fCountry.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fCountryCode.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fLocation.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fTransmissionReference.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fHeadline.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fCredit.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fSource.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fCopyrightNotice.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| if (fDescription .NotEmpty () || |
| fDescriptionWriter.NotEmpty ()) |
| { |
| return false; |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_iptc::ParseString (dng_stream &stream, |
| dng_string &s, |
| CharSet charSet) |
| { |
| |
| uint32 length = stream.Get_uint16 (); |
| |
| dng_memory_data buffer (length + 1); |
| |
| char *c = buffer.Buffer_char (); |
| |
| stream.Get (c, length); |
| |
| c [length] = 0; |
| |
| switch (charSet) |
| { |
| |
| case kCharSetUTF8: |
| { |
| s.Set_UTF8 (c); |
| break; |
| } |
| |
| default: |
| { |
| s.Set_SystemEncoding (c); |
| } |
| |
| } |
| |
| s.SetLineEndingsToNewLines (); |
| |
| s.StripLowASCII (); |
| |
| s.TrimTrailingBlanks (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_iptc::Parse (const void *blockData, |
| uint32 blockSize, |
| uint64 offsetInOriginalFile) |
| { |
| |
| dng_stream stream (blockData, |
| blockSize, |
| offsetInOriginalFile); |
| |
| stream.SetBigEndian (); |
| |
| // Make a first pass though the data, trying to figure out the |
| // character set. |
| |
| CharSet charSet = kCharSetUnknown; |
| |
| bool isValidUTF8 = true; |
| |
| bool hasEncodingMarker = false; |
| |
| uint64 firstOffset = stream.Position (); |
| |
| uint64 nextOffset = firstOffset; |
| |
| while (nextOffset + 5 < stream.Length ()) |
| { |
| |
| stream.SetReadPosition (nextOffset); |
| |
| uint8 firstByte = stream.Get_uint8 (); |
| |
| if (firstByte != 0x1C) break; |
| |
| uint8 record = stream.Get_uint8 (); |
| uint8 dataSet = stream.Get_uint8 (); |
| uint32 dataSize = stream.Get_uint16 (); |
| |
| nextOffset = stream.Position () + dataSize; |
| |
| if (record == 1) |
| { |
| |
| switch (dataSet) |
| { |
| |
| case 90: |
| { |
| |
| hasEncodingMarker = true; |
| |
| if (dataSize == 3) |
| { |
| |
| uint32 byte1 = stream.Get_uint8 (); |
| uint32 byte2 = stream.Get_uint8 (); |
| uint32 byte3 = stream.Get_uint8 (); |
| |
| if (byte1 == 27 /* Escape */ && |
| byte2 == 0x25 && |
| byte3 == 0x47) |
| { |
| |
| charSet = kCharSetUTF8; |
| |
| } |
| |
| } |
| |
| break; |
| |
| } |
| |
| default: |
| break; |
| |
| } |
| |
| } |
| |
| else if (record == 2) |
| { |
| |
| dng_memory_data buffer (dataSize + 1); |
| |
| char *s = buffer.Buffer_char (); |
| |
| stream.Get (s, dataSize); |
| |
| s [dataSize] = 0; |
| |
| isValidUTF8 = isValidUTF8 && dng_string::IsUTF8 (s); |
| |
| } |
| |
| } |
| |
| // If we don't have an encoding marker, and the data is valid |
| // UTF-8, then assume that it is UTF-8 (rather than system encoding). |
| |
| if (!hasEncodingMarker && isValidUTF8) |
| { |
| |
| charSet = kCharSetUTF8; |
| |
| } |
| |
| // Make a second pass though the data, actually reading the data. |
| |
| nextOffset = firstOffset; |
| |
| while (nextOffset + 5 < stream.Length ()) |
| { |
| |
| stream.SetReadPosition (nextOffset); |
| |
| uint8 firstByte = stream.Get_uint8 (); |
| |
| if (firstByte != 0x1C) break; |
| |
| uint8 record = stream.Get_uint8 (); |
| uint8 dataSet = stream.Get_uint8 (); |
| uint32 dataSize = stream.Get_uint16 (); |
| |
| nextOffset = stream.Position () + dataSize; |
| |
| if (record == 2) |
| { |
| |
| stream.SetReadPosition (stream.Position () - 2); |
| |
| switch ((DataSet) dataSet) |
| { |
| |
| case kObjectNameSet: |
| { |
| ParseString (stream, fTitle, charSet); |
| break; |
| } |
| |
| case kUrgencySet: |
| { |
| |
| int32 size = stream.Get_uint16 (); |
| |
| if (size == 1) |
| { |
| |
| char c = stream.Get_int8 (); |
| |
| if (c >= '0' && c <= '9') |
| { |
| fUrgency = c - '0'; |
| } |
| |
| } |
| |
| break; |
| |
| } |
| |
| case kCategorySet: |
| { |
| ParseString (stream, fCategory, charSet); |
| break; |
| } |
| |
| case kSupplementalCategoriesSet: |
| { |
| |
| dng_string category; |
| |
| ParseString (stream, category, charSet); |
| |
| if (category.NotEmpty ()) |
| { |
| fSupplementalCategories.Append (category); |
| } |
| |
| break; |
| |
| } |
| |
| case kKeywordsSet: |
| { |
| |
| dng_string keyword; |
| |
| ParseString (stream, keyword, charSet); |
| |
| if (keyword.NotEmpty ()) |
| { |
| fKeywords.Append (keyword); |
| } |
| |
| break; |
| |
| } |
| |
| case kSpecialInstructionsSet: |
| { |
| ParseString (stream, fInstructions, charSet); |
| break; |
| } |
| |
| case kDateCreatedSet: |
| { |
| |
| uint32 length = stream.Get_uint16 (); |
| |
| if (length == 8) |
| { |
| |
| char date [9]; |
| |
| stream.Get (date, 8); |
| |
| date [8] = 0; |
| |
| fDateTimeCreated.Decode_IPTC_Date (date); |
| |
| } |
| |
| break; |
| |
| } |
| |
| case kTimeCreatedSet: |
| { |
| |
| uint32 length = stream.Get_uint16 (); |
| |
| if (length >= 4 && length <= 11) |
| { |
| |
| char time [12]; |
| |
| stream.Get (time, length); |
| |
| time [length] = 0; |
| |
| fDateTimeCreated.Decode_IPTC_Time (time); |
| |
| } |
| |
| break; |
| |
| } |
| |
| case kDigitalCreationDateSet: |
| { |
| |
| uint32 length = stream.Get_uint16 (); |
| |
| if (length == 8) |
| { |
| |
| char date [9]; |
| |
| stream.Get (date, 8); |
| |
| date [8] = 0; |
| |
| fDigitalCreationDateTime.Decode_IPTC_Date (date); |
| |
| } |
| |
| break; |
| |
| } |
| |
| case kDigitalCreationTimeSet: |
| { |
| |
| uint32 length = stream.Get_uint16 (); |
| |
| if (length >= 4 && length <= 11) |
| { |
| |
| char time [12]; |
| |
| stream.Get (time, length); |
| |
| time [length] = 0; |
| |
| fDigitalCreationDateTime.Decode_IPTC_Time (time); |
| |
| } |
| |
| break; |
| |
| } |
| |
| case kBylineSet: |
| { |
| |
| dng_string author; |
| |
| ParseString (stream, author, charSet); |
| |
| if (author.NotEmpty ()) |
| { |
| fAuthors.Append (author); |
| } |
| |
| break; |
| |
| } |
| |
| case kBylineTitleSet: |
| { |
| ParseString (stream, fAuthorsPosition, charSet); |
| break; |
| } |
| |
| case kCitySet: |
| { |
| ParseString (stream, fCity, charSet); |
| break; |
| } |
| |
| case kProvinceStateSet: |
| { |
| ParseString (stream, fState, charSet); |
| break; |
| } |
| |
| case kCountryNameSet: |
| { |
| ParseString (stream, fCountry, charSet); |
| break; |
| } |
| |
| case kCountryCodeSet: |
| { |
| ParseString (stream, fCountryCode, charSet); |
| break; |
| } |
| |
| case kSublocationSet: |
| { |
| ParseString (stream, fLocation, charSet); |
| break; |
| } |
| |
| case kOriginalTransmissionReferenceSet: |
| { |
| ParseString (stream, fTransmissionReference, charSet); |
| break; |
| } |
| |
| case kHeadlineSet: |
| { |
| ParseString (stream, fHeadline, charSet); |
| break; |
| } |
| |
| case kCreditSet: |
| { |
| ParseString (stream, fCredit, charSet); |
| break; |
| } |
| |
| case kSourceSet: |
| { |
| ParseString (stream, fSource, charSet); |
| break; |
| } |
| |
| case kCopyrightNoticeSet: |
| { |
| ParseString (stream, fCopyrightNotice, charSet); |
| break; |
| } |
| |
| case kCaptionSet: |
| { |
| ParseString (stream, fDescription, charSet); |
| break; |
| } |
| |
| case kCaptionWriterSet: |
| { |
| ParseString (stream, fDescriptionWriter, charSet); |
| break; |
| } |
| |
| // All other IPTC records are not part of the IPTC core |
| // and/or are not kept in sync with XMP tags, so we ignore |
| // them. |
| |
| default: |
| break; |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_iptc::SpoolString (dng_stream &stream, |
| const dng_string &s, |
| uint8 dataSet, |
| uint32 maxChars, |
| CharSet charSet) |
| { |
| |
| if (s.IsEmpty ()) |
| { |
| return; |
| } |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (dataSet); |
| |
| dng_string ss (s); |
| |
| ss.SetLineEndingsToReturns (); |
| |
| if (charSet == kCharSetUTF8) |
| { |
| |
| // UTF-8 encoding. |
| |
| if (ss.Length () > maxChars) |
| { |
| ss.Truncate (maxChars); |
| } |
| |
| uint32 len = ss.Length (); |
| |
| stream.Put_uint16 ((uint16) len); |
| |
| stream.Put (ss.Get (), len); |
| |
| } |
| |
| else |
| { |
| |
| // System character set encoding. |
| |
| dng_memory_data buffer; |
| |
| uint32 len = ss.Get_SystemEncoding (buffer); |
| |
| if (len > maxChars) |
| { |
| |
| uint32 lower = 0; |
| uint32 upper = ss.Length () - 1; |
| |
| while (upper > lower) |
| { |
| |
| uint32 middle = (upper + lower + 1) >> 1; |
| |
| dng_string sss (ss); |
| |
| sss.Truncate (middle); |
| |
| len = sss.Get_SystemEncoding (buffer); |
| |
| if (len <= maxChars) |
| { |
| |
| lower = middle; |
| |
| } |
| |
| else |
| { |
| |
| upper = middle - 1; |
| |
| } |
| |
| } |
| |
| ss.Truncate (lower); |
| |
| len = ss.Get_SystemEncoding (buffer); |
| |
| } |
| |
| stream.Put_uint16 ((uint16) len); |
| |
| stream.Put (buffer.Buffer_char (), len); |
| |
| } |
| |
| } |
| /*****************************************************************************/ |
| |
| dng_memory_block * dng_iptc::Spool (dng_memory_allocator &allocator, |
| bool padForTIFF) |
| { |
| |
| uint32 j; |
| |
| char s [64]; |
| |
| dng_memory_stream stream (allocator, NULL, 2048); |
| |
| stream.SetBigEndian (); |
| |
| // Medata working group - now we just always write UTF-8. |
| |
| CharSet charSet = kCharSetUTF8; |
| |
| // UTF-8 encoding marker. |
| |
| if (charSet == kCharSetUTF8) |
| { |
| |
| stream.Put_uint16 (0x1C01); |
| stream.Put_uint8 (90); |
| stream.Put_uint16 (3); |
| stream.Put_uint8 (27); |
| stream.Put_uint8 (0x25); |
| stream.Put_uint8 (0x47); |
| |
| } |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (kRecordVersionSet); |
| stream.Put_uint16 (2); |
| stream.Put_uint16 (4); |
| |
| SpoolString (stream, |
| fTitle, |
| kObjectNameSet, |
| 64, |
| charSet); |
| |
| if (fUrgency >= 0) |
| { |
| |
| sprintf (s, "%1u", (unsigned) fUrgency); |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (kUrgencySet); |
| |
| stream.Put_uint16 (1); |
| |
| stream.Put (s, 1); |
| |
| } |
| |
| SpoolString (stream, |
| fCategory, |
| kCategorySet, |
| 3, |
| charSet); |
| |
| for (j = 0; j < fSupplementalCategories.Count (); j++) |
| { |
| |
| SpoolString (stream, |
| fSupplementalCategories [j], |
| kSupplementalCategoriesSet, |
| 32, |
| charSet); |
| |
| } |
| |
| for (j = 0; j < fKeywords.Count (); j++) |
| { |
| |
| SpoolString (stream, |
| fKeywords [j], |
| kKeywordsSet, |
| 64, |
| charSet); |
| |
| } |
| |
| SpoolString (stream, |
| fInstructions, |
| kSpecialInstructionsSet, |
| 255, |
| charSet); |
| |
| if (fDateTimeCreated.IsValid ()) |
| { |
| |
| dng_string dateString = fDateTimeCreated.Encode_IPTC_Date (); |
| |
| if (dateString.NotEmpty ()) |
| { |
| |
| DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (kDateCreatedSet); |
| |
| stream.Put_uint16 (8); |
| |
| stream.Put (dateString.Get (), 8); |
| |
| } |
| |
| dng_string timeString = fDateTimeCreated.Encode_IPTC_Time (); |
| |
| if (timeString.NotEmpty ()) |
| { |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (kTimeCreatedSet); |
| |
| stream.Put_uint16 ((uint16)timeString.Length ()); |
| |
| stream.Put (timeString.Get (), timeString.Length ()); |
| |
| } |
| |
| } |
| |
| if (fDigitalCreationDateTime.IsValid ()) |
| { |
| |
| dng_string dateString = fDigitalCreationDateTime.Encode_IPTC_Date (); |
| |
| if (dateString.NotEmpty ()) |
| { |
| |
| DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (kDigitalCreationDateSet); |
| |
| stream.Put_uint16 (8); |
| |
| stream.Put (dateString.Get (), 8); |
| |
| } |
| |
| dng_string timeString = fDigitalCreationDateTime.Encode_IPTC_Time (); |
| |
| if (timeString.NotEmpty ()) |
| { |
| |
| stream.Put_uint16 (0x1C02); |
| stream.Put_uint8 (kDigitalCreationTimeSet); |
| |
| stream.Put_uint16 ((uint16)timeString.Length ()); |
| |
| stream.Put (timeString.Get (), timeString.Length ()); |
| |
| } |
| |
| } |
| |
| for (j = 0; j < fAuthors.Count (); j++) |
| { |
| |
| SpoolString (stream, |
| fAuthors [j], |
| kBylineSet, |
| 32, |
| charSet); |
| |
| } |
| |
| SpoolString (stream, |
| fAuthorsPosition, |
| kBylineTitleSet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fCity, |
| kCitySet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fLocation, |
| kSublocationSet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fState, |
| kProvinceStateSet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fCountryCode, |
| kCountryCodeSet, |
| 3, |
| charSet); |
| |
| SpoolString (stream, |
| fCountry, |
| kCountryNameSet, |
| 64, |
| charSet); |
| |
| SpoolString (stream, |
| fTransmissionReference, |
| kOriginalTransmissionReferenceSet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fHeadline, |
| kHeadlineSet, |
| 255, |
| charSet); |
| |
| SpoolString (stream, |
| fCredit, |
| kCreditSet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fSource, |
| kSourceSet, |
| 32, |
| charSet); |
| |
| SpoolString (stream, |
| fCopyrightNotice, |
| kCopyrightNoticeSet, |
| 128, |
| charSet); |
| |
| SpoolString (stream, |
| fDescription, |
| kCaptionSet, |
| 2000, |
| charSet); |
| |
| SpoolString (stream, |
| fDescriptionWriter, |
| kCaptionWriterSet, |
| 32, |
| charSet); |
| |
| if (padForTIFF) |
| { |
| |
| while (stream.Length () & 3) |
| { |
| stream.Put_uint8 (0); |
| } |
| |
| } |
| |
| stream.Flush (); |
| |
| return stream.AsMemoryBlock (allocator); |
| |
| } |
| |
| /*****************************************************************************/ |