C++程序  |  986行  |  15.82 KB

/*****************************************************************************/
// 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);
	
	}
					
/*****************************************************************************/