Example #1
0
void dng_info::ParseDNGPrivateData (dng_host &host,
									dng_stream &stream)
	{
	
	if (fShared->fDNGPrivateDataCount < 2)
		{
		return;
		}
	
	// DNG private data should always start with a null-terminated 
	// company name, to define the format of the private data.
			
	dng_string privateName;
			
		{
			
		char buffer [64];
		
		stream.SetReadPosition (fShared->fDNGPrivateDataOffset);
	
		uint32 readLength = Min_uint32 (fShared->fDNGPrivateDataCount,
										sizeof (buffer) - 1);
		
		stream.Get (buffer, readLength);
		
		buffer [readLength] = 0;
		
		privateName.Set (buffer);
		
		}
		
	// Pentax is storing their MakerNote in the DNGPrivateData data.
	
	if (privateName.StartsWith ("PENTAX" ) ||
		privateName.StartsWith ("SAMSUNG"))
		{
		
		#if qDNGValidate
		
		if (gVerbose)
			{
			printf ("Parsing Pentax/Samsung DNGPrivateData\n\n");
			}
			
		#endif

		stream.SetReadPosition (fShared->fDNGPrivateDataOffset + 8);
		
		bool bigEndian = stream.BigEndian ();
		
		uint16 endianMark = stream.Get_uint16 ();
		
		if (endianMark == byteOrderMM)
			{
			bigEndian = true;
			}
			
		else if (endianMark == byteOrderII)
			{
			bigEndian = false;
			}
			
		TempBigEndian temp_endian (stream, bigEndian);
	
		ParseMakerNoteIFD (host,
						   stream,
						   fShared->fDNGPrivateDataCount - 10,
						   fShared->fDNGPrivateDataOffset + 10,
						   fShared->fDNGPrivateDataOffset,
						   fShared->fDNGPrivateDataOffset,
						   fShared->fDNGPrivateDataOffset + fShared->fDNGPrivateDataCount,
						   tcPentaxMakerNote);
						   
		return;
		
		}
				
	// Stop parsing if this is not an Adobe format block.
	
	if (!privateName.Matches ("Adobe"))
		{
		return;
		}
	
	TempBigEndian temp_order (stream);
	
	uint32 section_offset = 6;
	
	while (section_offset + 8 < fShared->fDNGPrivateDataCount)
		{
		
		stream.SetReadPosition (fShared->fDNGPrivateDataOffset + section_offset);
		
		uint32 section_key   = stream.Get_uint32 ();
		uint32 section_count = stream.Get_uint32 ();
		
		if (section_key == DNG_CHAR4 ('M','a','k','N') && section_count > 6)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found MakerNote inside DNGPrivateData\n\n");
				}
				
			#endif
				
			uint16 order_mark = stream.Get_uint16 ();
			uint64 old_offset = stream.Get_uint32 ();

			uint32 tempSize = section_count - 6;
			
			AutoPtr<dng_memory_block> tempBlock (host.Allocate (tempSize));
			
			uint64 positionInOriginalFile = stream.PositionInOriginalFile();
			
			stream.Get (tempBlock->Buffer (), tempSize);
			
			dng_stream tempStream (tempBlock->Buffer (),
								   tempSize,
								   positionInOriginalFile);
								   
			tempStream.SetBigEndian (order_mark == byteOrderMM);
			
			ParseMakerNote (host,
							tempStream,
							tempSize,
							0,
							0 - old_offset,
							0,
							tempSize);
	
			}
			
		else if (section_key == DNG_CHAR4 ('S','R','2',' ') && section_count > 6)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Sony private data inside DNGPrivateData\n\n");
				}
				
			#endif
			
			uint16 order_mark = stream.Get_uint16 ();
			uint64 old_offset = stream.Get_uint32 ();

			uint64 new_offset = fShared->fDNGPrivateDataOffset + section_offset + 14;
			
			TempBigEndian sr2_order (stream, order_mark == byteOrderMM);
			
			ParseSonyPrivateData (host,
							  	  stream,
								  section_count - 6,
								  old_offset,
								  new_offset);
				
			}

		else if (section_key == DNG_CHAR4 ('R','A','F',' ') && section_count > 4)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Fuji RAF tags inside DNGPrivateData\n\n");
				}
				
			#endif
			
			uint16 order_mark = stream.Get_uint16 ();
			
			uint32 tagCount = stream.Get_uint32 ();
			
			uint64 tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian raf_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcFujiRAF,
						  tcFujiHeader,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				stream.SetReadPosition (tagOffset + tagCount);
				
				}
			
			tagCount = stream.Get_uint32 ();
			
			tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian raf_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcFujiRAF,
						  tcFujiRawInfo1,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				stream.SetReadPosition (tagOffset + tagCount);
				
				}
			
			tagCount = stream.Get_uint32 ();
			
			tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian raf_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcFujiRAF,
						  tcFujiRawInfo2,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				stream.SetReadPosition (tagOffset + tagCount);
				
				}
			
			}

		else if (section_key == DNG_CHAR4 ('C','n','t','x') && section_count > 4)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Contax Raw header inside DNGPrivateData\n\n");
				}
				
			#endif
			
			uint16 order_mark = stream.Get_uint16 ();
			
			uint32 tagCount  = stream.Get_uint32 ();
			
			uint64 tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian contax_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcContaxRAW,
						  tcContaxHeader,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				}
			
			}
			
		else if (section_key == DNG_CHAR4 ('C','R','W',' ') && section_count > 4)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Canon CRW tags inside DNGPrivateData\n\n");
				}
				
			#endif
				
			uint16 order_mark = stream.Get_uint16 ();
			uint32 entries    = stream.Get_uint16 ();
			
			uint64 crwTagStart = stream.Position ();
			
			for (uint32 parsePass = 1; parsePass <= 2; parsePass++)
				{
				
				stream.SetReadPosition (crwTagStart);
			
				for (uint32 index = 0; index < entries; index++)
					{
					
					uint32 tagCode = stream.Get_uint16 ();
											 
					uint32 tagCount = stream.Get_uint32 ();
					
					uint64 tagOffset = stream.Position ();
					
					// We need to grab the model id tag first, and then all the
					// other tags.
					
					if ((parsePass == 1) == (tagCode == 0x5834))
						{
				
						TempBigEndian tag_order (stream, order_mark == byteOrderMM);
					
						ParseTag (host,
								  stream,
								  fExif.Get (),
								  fShared.Get (),
								  NULL,
								  tcCanonCRW,
								  tagCode,
								  ttUndefined,
								  tagCount,
								  tagOffset,
								  0);
								  
						}
					
					stream.SetReadPosition (tagOffset + tagCount);
					
					}
					
				}
			
			}

		else if (section_count > 4)
			{
			
			uint32 parentCode = 0;
			
			bool code32  = false;
			bool hasType = true;
			
			switch (section_key)
				{
				
				case DNG_CHAR4 ('M','R','W',' '):
					{
					parentCode = tcMinoltaMRW;
					code32     = true;
					hasType    = false;
					break;
					}
				
				case DNG_CHAR4 ('P','a','n','o'):
					{
					parentCode = tcPanasonicRAW;
					break;
					}
					
				case DNG_CHAR4 ('L','e','a','f'):
					{
					parentCode = tcLeafMOS;
					break;
					}
					
				case DNG_CHAR4 ('K','o','d','a'):
					{
					parentCode = tcKodakDCRPrivateIFD;
					break;
					}
					
				case DNG_CHAR4 ('K','D','C',' '):
					{
					parentCode = tcKodakKDCPrivateIFD;
					break;
					}
					
				default:
					break;
					
				}

			if (parentCode)
				{
			
				#if qDNGValidate
				
				if (gVerbose)
					{
					printf ("Found %s tags inside DNGPrivateData\n\n",
							LookupParentCode (parentCode));
					}
					
				#endif
				
				uint16 order_mark = stream.Get_uint16 ();
				uint32 entries    = stream.Get_uint16 ();
				
				for (uint32 index = 0; index < entries; index++)
					{
					
					uint32 tagCode = code32 ? stream.Get_uint32 ()
											: stream.Get_uint16 ();
											 
					uint32 tagType  = hasType ? stream.Get_uint16 () 
											  : ttUndefined;
					
					uint32 tagCount = stream.Get_uint32 ();
					
					uint32 tagSize = tagCount * TagTypeSize (tagType);
					
					uint64 tagOffset = stream.Position ();
					
					TempBigEndian tag_order (stream, order_mark == byteOrderMM);
				
					ParseTag (host,
							  stream,
							  fExif.Get (),
							  fShared.Get (),
							  NULL,
							  parentCode,
							  tagCode,
							  tagType,
							  tagCount,
							  tagOffset,
							  0);
					
					stream.SetReadPosition (tagOffset + tagSize);
					
					}
					
				}
			
			}
		
		section_offset += 8 + section_count;
		
		if (section_offset & 1)
			{
			section_offset++;
			}
		
		}
		
	}
Example #2
0
void dng_info::ParseMakerNote (dng_host &host,
							   dng_stream &stream,
							   uint32 makerNoteCount,
							   uint64 makerNoteOffset,
							   int64 offsetDelta,
							   uint64 minOffset,
							   uint64 maxOffset)
	{
	
	uint8 firstBytes [16];
	
	memset (firstBytes, 0, sizeof (firstBytes));
	
	stream.SetReadPosition (makerNoteOffset);
	
	stream.Get (firstBytes, (uint32) Min_uint64 (sizeof (firstBytes),
												 makerNoteCount));
	
	// Epson MakerNote with header.
	
	if (memcmp (firstBytes, "EPSON\000\001\000", 8) == 0)
		{
		
		if (makerNoteCount > 8)
			{
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 8,
				   	  		   makerNoteOffset + 8,
				   	  		   offsetDelta,
				   	  		   minOffset,
				   	  		   maxOffset,
				   	  		   tcEpsonMakerNote);
				   	  		   
			}
			
		return;
		
		}
		
	// Fujifilm MakerNote.
	
	if (memcmp (firstBytes, "FUJIFILM", 8) == 0)
		{
		
		stream.SetReadPosition (makerNoteOffset + 8);
		
		TempLittleEndian tempEndian (stream);
		
		uint32 ifd_offset = stream.Get_uint32 ();
		
		if (ifd_offset >= 12 && ifd_offset < makerNoteCount)
			{
			
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - ifd_offset,
							   makerNoteOffset + ifd_offset,
							   makerNoteOffset,
							   minOffset,
							   maxOffset,
							   tcFujiMakerNote);
			
			}
			
		return;
					
		}
		
	// Leica MakerNote.
	
	if (memcmp (firstBytes, "LEICA\000\000\000", 8) == 0)
		{
		
		if (makerNoteCount > 8)
			{
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 8,
							   makerNoteOffset + 8,
							   makerNoteOffset,
							   minOffset,
							   maxOffset,
							   tcLeicaMakerNote);
							   
			}
		
		return;
		
		}
		
	// Nikon version 2 MakerNote with header.
	
	if (memcmp (firstBytes, "Nikon\000\002", 7) == 0)
		{
		
		stream.SetReadPosition (makerNoteOffset + 10);
		
		bool bigEndian = false;
		
		uint16 endianMark = stream.Get_uint16 ();
		
		if (endianMark == byteOrderMM)
			{
			bigEndian = true;
			}
			
		else if (endianMark != byteOrderII)
			{
			return;
			}
			
		TempBigEndian temp_endian (stream, bigEndian);
		
		uint16 magic = stream.Get_uint16 ();
		
		if (magic != 42)
			{
			return;
			}
			
		uint32 ifd_offset = stream.Get_uint32 ();
		
		if (ifd_offset >= 8 && ifd_offset < makerNoteCount - 10)
			{
			
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 10 - ifd_offset,
							   makerNoteOffset + 10 + ifd_offset,
							   makerNoteOffset + 10,
							   minOffset,
							   maxOffset,
							   tcNikonMakerNote);
			
			}
			
		return;
					
		}
		
	// Newer version of Olympus MakerNote with byte order mark.
	
	if (memcmp (firstBytes, "OLYMPUS\000", 8) == 0)
		{
		
		stream.SetReadPosition (makerNoteOffset + 8);
		
		bool bigEndian = false;
		
		uint16 endianMark = stream.Get_uint16 ();
		
		if (endianMark == byteOrderMM)
			{
			bigEndian = true;
			}
			
		else if (endianMark != byteOrderII)
			{
			return;
			}
			
		TempBigEndian temp_endian (stream, bigEndian);
		
		uint16 version = stream.Get_uint16 ();
		
		if (version != 3)
			{
			return;
			}
		
		if (makerNoteCount > 12)
			{
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 12,
				   	  		   makerNoteOffset + 12,
				   	  		   makerNoteOffset,
				   	  		   minOffset,
				   	  		   maxOffset,
				   	  		   tcOlympusMakerNote);
				   	  		   
			}
			
		return;
		
		}
		
	// Olympus MakerNote with header.
	
	if (memcmp (firstBytes, "OLYMP", 5) == 0)
		{
		
		if (makerNoteCount > 8)
			{
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 8,
				   	  		   makerNoteOffset + 8,
				   	  		   offsetDelta,
				   	  		   minOffset,
				   	  		   maxOffset,
				   	  		   tcOlympusMakerNote);
				   	  		   
			}
			
		return;
		
		}
		
	// Panasonic MakerNote.
	
	if (memcmp (firstBytes, "Panasonic\000\000\000", 12) == 0)
		{
		
		if (makerNoteCount > 12)
			{
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 12,
							   makerNoteOffset + 12,
							   offsetDelta,
							   minOffset,
							   maxOffset,
							   tcPanasonicMakerNote);
							   
			}
		
		return;
		
		}
		
	// Pentax MakerNote.
	
	if (memcmp (firstBytes, "AOC", 4) == 0)
		{
		
		if (makerNoteCount > 6)
			{
					
			stream.SetReadPosition (makerNoteOffset + 4);
			
			bool bigEndian = stream.BigEndian ();
			
			uint16 endianMark = stream.Get_uint16 ();
			
			if (endianMark == byteOrderMM)
				{
				bigEndian = true;
				}
				
			else if (endianMark == byteOrderII)
				{
				bigEndian = false;
				}
				
			TempBigEndian temp_endian (stream, bigEndian);
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 6,
							   makerNoteOffset + 6,
							   offsetDelta,
							   minOffset,
							   maxOffset,
							   tcPentaxMakerNote);
			
			}
			
		return;
		
		}
					
	// Ricoh MakerNote.
	
	if (memcmp (firstBytes, "RICOH", 5) == 0 ||
		memcmp (firstBytes, "Ricoh", 5) == 0)
		{
		
		if (makerNoteCount > 8)
			{
			
			TempBigEndian tempEndian (stream);
		
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount - 8,
				   	  		   makerNoteOffset + 8,
				   	  		   offsetDelta,
				   	  		   minOffset,
				   	  		   maxOffset,
				   	  		   tcRicohMakerNote);
				   	  		   
			}
			
		return;
		
		}
		
	// Nikon MakerNote without header.
	
	if (fExif->fMake.StartsWith ("NIKON"))
		{
		
		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
			   	  		   makerNoteOffset,
			   	  		   offsetDelta,
			   	  		   minOffset,
			   	  		   maxOffset,
			   	  		   tcNikonMakerNote);
			   	  		   
		return;
			
		}
	
	// Canon MakerNote.
	
	if (fExif->fMake.StartsWith ("CANON"))
		{
		
		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
			   	  		   makerNoteOffset,
			   	  		   offsetDelta,
			   	  		   minOffset,
			   	  		   maxOffset,
			   	  		   tcCanonMakerNote);
			
		return;
		
		}
		
	// Minolta MakerNote.
	
	if (fExif->fMake.StartsWith ("MINOLTA"       ) ||
		fExif->fMake.StartsWith ("KONICA MINOLTA"))
		{

		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
						   makerNoteOffset,
						   offsetDelta,
						   minOffset,
						   maxOffset,
						   tcMinoltaMakerNote);
			
		return;
		
		}
	
	// Sony MakerNote.
	
	if (fExif->fMake.StartsWith ("SONY"))
		{

		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
						   makerNoteOffset,
						   offsetDelta,
						   minOffset,
						   maxOffset,
						   tcSonyMakerNote);
			
		return;
		
		}
	
	// Kodak MakerNote.
	
	if (fExif->fMake.StartsWith ("EASTMAN KODAK"))
		{
		
		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
			   	  		   makerNoteOffset,
			   	  		   offsetDelta,
			   	  		   minOffset,
			   	  		   maxOffset,
			   	  		   tcKodakMakerNote);
			   	  		   
		return;
			
		}
	
	// Mamiya MakerNote.
	
	if (fExif->fMake.StartsWith ("Mamiya"))
		{
		
		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
			   	  		   makerNoteOffset,
			   	  		   offsetDelta,
			   	  		   minOffset,
			   	  		   maxOffset,
			   	  		   tcMamiyaMakerNote);
						   
		// Mamiya uses a MakerNote chain.
						   
		while (fMakerNoteNextIFD)
			{
			   	  		   
			ParseMakerNoteIFD (host,
							   stream,
							   makerNoteCount,
							   offsetDelta + fMakerNoteNextIFD,
							   offsetDelta,
							   minOffset,
							   maxOffset,
							   tcMamiyaMakerNote);
							   
			}
			   	  		   
		return;
			
		}
	
	// Nikon MakerNote without header.
	
	if (fExif->fMake.StartsWith ("Hasselblad"))
		{
		
		ParseMakerNoteIFD (host,
						   stream,
						   makerNoteCount,
			   	  		   makerNoteOffset,
			   	  		   offsetDelta,
			   	  		   minOffset,
			   	  		   maxOffset,
			   	  		   tcHasselbladMakerNote);
			   	  		   
		return;
			
		}
	
	}
Example #3
0
bool dng_camera_profile_info::ParseTag (dng_stream &stream,
										uint32 parentCode,
										uint32 tagCode,
										uint32 tagType,
										uint32 tagCount,
										uint64 tagOffset)
	{

	switch (tagCode)
		{

		case tcCalibrationIlluminant1:
			{

			CheckTagType (parentCode, tagCode, tagType, ttShort);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fCalibrationIlluminant1 = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CalibrationIlluminant1: %s\n",
						LookupLightSource (fCalibrationIlluminant1));

				}

			#endif

			break;

			}

		case tcCalibrationIlluminant2:
			{

			CheckTagType (parentCode, tagCode, tagType, ttShort);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fCalibrationIlluminant2 = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CalibrationIlluminant2: %s\n",
						LookupLightSource (fCalibrationIlluminant2));

				}

			#endif

			break;

			}

		case tcColorMatrix1:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (fColorPlanes == 0)
				{

				fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes);

				}

			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 fColorPlanes,
								 3,
								 fColorMatrix1))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ColorMatrix1:\n");

				DumpMatrix (fColorMatrix1);

				}

			#endif

			break;

			}

		case tcColorMatrix2:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 fColorPlanes,
								 3,
								 fColorMatrix2))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ColorMatrix2:\n");

				DumpMatrix (fColorMatrix2);

				}

			#endif

			break;

			}

		case tcForwardMatrix1:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 3,
								 fColorPlanes,
								 fForwardMatrix1))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ForwardMatrix1:\n");

				DumpMatrix (fForwardMatrix1);

				}

			#endif

			break;

			}

		case tcForwardMatrix2:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 3,
								 fColorPlanes,
								 fForwardMatrix2))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ForwardMatrix2:\n");

				DumpMatrix (fForwardMatrix2);

				}

			#endif

			break;

			}

		case tcReductionMatrix1:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 3,
								 fColorPlanes,
								 fReductionMatrix1))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ReductionMatrix1:\n");

				DumpMatrix (fReductionMatrix1);

				}

			#endif

			break;

			}

		case tcReductionMatrix2:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 3,
								 fColorPlanes,
								 fReductionMatrix2))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ReductionMatrix2:\n");

				DumpMatrix (fReductionMatrix2);

				}

			#endif

			break;

			}

		case tcProfileCalibrationSignature:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fProfileCalibrationSignature,
							false,
							false);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileCalibrationSignature: ");

				DumpString (fProfileCalibrationSignature);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcProfileName:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fProfileName,
							false,
							false);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileName: ");

				DumpString (fProfileName);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcProfileCopyright:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fProfileCopyright,
							false,
							false);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileCopyright: ");

				DumpString (fProfileCopyright);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcProfileEmbedPolicy:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fEmbedPolicy = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				const char *policy;

				switch (fEmbedPolicy)
					{

					case pepAllowCopying:
						policy = "Allow copying";
						break;

					case pepEmbedIfUsed:
						policy = "Embed if used";
						break;

					case pepEmbedNever:
						policy = "Embed never";
						break;

					case pepNoRestrictions:
						policy = "No restrictions";
						break;

					default:
						policy = "INVALID VALUE";

					}

				printf ("ProfileEmbedPolicy: %s\n", policy);

				}

			#endif

			break;

			}

		case tcProfileHueSatMapDims:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong);

			CheckTagCount (parentCode, tagCode, tagCount, 2, 3);

			fProfileHues = stream.TagValue_uint32 (tagType);
			fProfileSats = stream.TagValue_uint32 (tagType);

			if (tagCount > 2)
				fProfileVals = stream.TagValue_uint32 (tagType);
			else
				fProfileVals = 1;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileHueSatMapDims: Hues = %u, Sats = %u, Vals = %u\n",
						(unsigned) fProfileHues,
						(unsigned) fProfileSats,
						(unsigned) fProfileVals);

				}

			#endif

			break;

			}

		case tcProfileHueSatMapData1:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
				return false;

			bool skipSat0 = (tagCount == fProfileHues *
										(fProfileSats - 1) *
										 fProfileVals * 3);

			if (!skipSat0)
				{

				if (!CheckTagCount (parentCode, tagCode, tagCount, fProfileHues *
																   fProfileSats *
																   fProfileVals * 3))
					return false;

				}

			fBigEndian = stream.BigEndian ();

			fHueSatDeltas1Offset = tagOffset;
			fHueSatDeltas1Count  = tagCount;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileHueSatMapData1:\n");

				DumpHueSatMap (stream,
							   fProfileHues,
							   fProfileSats,
							   fProfileVals,
							   skipSat0);

				}

			#endif

			break;

			}

		case tcProfileHueSatMapData2:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
				return false;

			bool skipSat0 = (tagCount == fProfileHues *
										(fProfileSats - 1) *
										 fProfileVals * 3);

			if (!skipSat0)
				{

				if (!CheckTagCount (parentCode, tagCode, tagCount, fProfileHues *
																   fProfileSats *
																   fProfileVals * 3))
					return false;

				}

			fBigEndian = stream.BigEndian ();

			fHueSatDeltas2Offset = tagOffset;
			fHueSatDeltas2Count  = tagCount;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileHueSatMapData2:\n");

				DumpHueSatMap (stream,
							   fProfileHues,
							   fProfileSats,
							   fProfileVals,
							   skipSat0);

				}

			#endif

			break;

			}

		case tcProfileLookTableDims:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong);

			CheckTagCount (parentCode, tagCode, tagCount, 2, 3);

			fLookTableHues = stream.TagValue_uint32 (tagType);
			fLookTableSats = stream.TagValue_uint32 (tagType);

			if (tagCount > 2)
				fLookTableVals = stream.TagValue_uint32 (tagType);
			else
				fLookTableVals = 1;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileLookTableDims: Hues = %u, Sats = %u, Vals = %u\n",
						(unsigned) fLookTableHues,
						(unsigned) fLookTableSats,
						(unsigned) fLookTableVals);

				}

			#endif

			break;

			}

		case tcProfileLookTableData:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
				return false;

			bool skipSat0 = (tagCount == fLookTableHues *
										(fLookTableSats - 1) *
										 fLookTableVals * 3);

			if (!skipSat0)
				{

				if (!CheckTagCount (parentCode, tagCode, tagCount, fLookTableHues *
																   fLookTableSats *
																   fLookTableVals * 3))
					return false;

				}

			fBigEndian = stream.BigEndian ();

			fLookTableOffset = tagOffset;
			fLookTableCount  = tagCount;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ProfileLookTableData:\n");

				DumpHueSatMap (stream,
							   fLookTableHues,
							   fLookTableSats,
							   fLookTableVals,
							   skipSat0);

				}

			#endif

			break;

			}

		case tcProfileToneCurve:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
				return false;

			if (!CheckTagCount (parentCode, tagCode, tagCount, 4, tagCount))
				return false;

			if ((tagCount & 1) != 0)
				{

				#if qDNGValidate

					{

					char message [256];

					sprintf (message,
							 "%s %s has odd count (%u)",
							 LookupParentCode (parentCode),
							 LookupTagCode (parentCode, tagCode),
							 (unsigned) tagCount);

					ReportWarning (message);

					}

				#endif

				return false;

				}

			fBigEndian = stream.BigEndian ();

			fToneCurveOffset = tagOffset;
			fToneCurveCount  = tagCount;

			#if qDNGValidate

			if (gVerbose)
				{

				DumpTagValues (stream,
							   "Coord",
							   parentCode,
							   tagCode,
							   tagType,
							   tagCount);


				}

			#endif

			break;

			}

		case tcUniqueCameraModel:
			{

			// Note: This code is only used when parsing stand-alone
			// profiles.  The embedded profiles are assumed to be restricted
			// to the model they are embedded in.

			CheckTagType (parentCode, tagCode, tagType, ttAscii);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fUniqueCameraModel,
							false);

			bool didTrim = fUniqueCameraModel.TrimTrailingBlanks ();

			#if qDNGValidate

			if (didTrim)
				{

				ReportWarning ("UniqueCameraModel string has trailing blanks");

				}

			if (gVerbose)
				{

				printf ("UniqueCameraModel: ");

				DumpString (fUniqueCameraModel);

				printf ("\n");

				}

			#else

			(void) didTrim;		// Unused

			#endif

			break;

			}

		default:
			{

			return false;

			}

		}

	return true;

	}