1/*****************************************************************************/
2// Copyright 2006-2008 Adobe Systems Incorporated
3// All Rights Reserved.
4//
5// NOTICE:  Adobe permits you to use, modify, and distribute this file in
6// accordance with the terms of the Adobe license agreement accompanying it.
7/*****************************************************************************/
8
9/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_iptc.cpp#1 $ */
10/* $DateTime: 2012/05/30 13:28:51 $ */
11/* $Change: 832332 $ */
12/* $Author: tknoll $ */
13
14/*****************************************************************************/
15
16#include "dng_iptc.h"
17
18#include "dng_assertions.h"
19#include "dng_auto_ptr.h"
20#include "dng_memory_stream.h"
21#include "dng_stream.h"
22#include "dng_utils.h"
23
24/*****************************************************************************/
25
26dng_iptc::dng_iptc ()
27
28	:	fTitle ()
29
30	,	fUrgency (-1)
31
32	,	fCategory ()
33
34	,	fSupplementalCategories ()
35
36	,	fKeywords ()
37
38	,	fInstructions ()
39
40	,	fDateTimeCreated ()
41
42	,	fDigitalCreationDateTime ()
43
44	,	fAuthors         ()
45	,	fAuthorsPosition ()
46
47	,	fCity        ()
48	,	fState       ()
49	,	fCountry     ()
50	,	fCountryCode ()
51
52	,	fLocation ()
53
54	,	fTransmissionReference ()
55
56	,	fHeadline ()
57
58	,	fCredit ()
59
60	,	fSource ()
61
62	,	fCopyrightNotice ()
63
64	,	fDescription       ()
65	,	fDescriptionWriter ()
66
67	{
68
69	}
70
71/*****************************************************************************/
72
73dng_iptc::~dng_iptc ()
74	{
75
76	}
77
78/*****************************************************************************/
79
80bool dng_iptc::IsEmpty () const
81	{
82
83	if (fTitle.NotEmpty ())
84		{
85		return false;
86		}
87
88	if (fUrgency >= 0)
89		{
90		return false;
91		}
92
93	if (fCategory.NotEmpty ())
94		{
95		return false;
96		}
97
98	if (fSupplementalCategories.Count () > 0)
99		{
100		return false;
101		}
102
103	if (fKeywords.Count () > 0)
104		{
105		return false;
106		}
107
108	if (fInstructions.NotEmpty ())
109		{
110		return false;
111		}
112
113	if (fDateTimeCreated.IsValid ())
114		{
115		return false;
116		}
117
118	if (fDigitalCreationDateTime.IsValid ())
119		{
120		return false;
121		}
122
123	if (fAuthors.Count () != 0 ||
124		fAuthorsPosition.NotEmpty ())
125		{
126		return false;
127		}
128
129	if (fCity   .NotEmpty () ||
130		fState  .NotEmpty () ||
131		fCountry.NotEmpty ())
132		{
133		return false;
134		}
135
136	if (fCountryCode.NotEmpty ())
137		{
138		return false;
139		}
140
141	if (fLocation.NotEmpty ())
142		{
143		return false;
144		}
145
146	if (fTransmissionReference.NotEmpty ())
147		{
148		return false;
149		}
150
151	if (fHeadline.NotEmpty ())
152		{
153		return false;
154		}
155
156	if (fCredit.NotEmpty ())
157		{
158		return false;
159		}
160
161	if (fSource.NotEmpty ())
162		{
163		return false;
164		}
165
166	if (fCopyrightNotice.NotEmpty ())
167		{
168		return false;
169		}
170
171	if (fDescription      .NotEmpty () ||
172		fDescriptionWriter.NotEmpty ())
173		{
174		return false;
175		}
176
177	return true;
178
179	}
180
181/*****************************************************************************/
182
183void dng_iptc::ParseString (dng_stream &stream,
184						    dng_string &s,
185						    CharSet charSet)
186	{
187
188	uint32 length = stream.Get_uint16 ();
189
190	dng_memory_data buffer (length + 1);
191
192	char *c = buffer.Buffer_char ();
193
194	stream.Get (c, length);
195
196	c [length] = 0;
197
198	switch (charSet)
199		{
200
201		case kCharSetUTF8:
202			{
203			s.Set_UTF8 (c);
204			break;
205			}
206
207		default:
208			{
209			s.Set_SystemEncoding (c);
210			}
211
212		}
213
214	s.SetLineEndingsToNewLines ();
215
216	s.StripLowASCII ();
217
218	s.TrimTrailingBlanks ();
219
220	}
221
222/*****************************************************************************/
223
224void dng_iptc::Parse (const void *blockData,
225					  uint32 blockSize,
226					  uint64 offsetInOriginalFile)
227	{
228
229	dng_stream stream (blockData,
230					   blockSize,
231					   offsetInOriginalFile);
232
233	stream.SetBigEndian ();
234
235	// Make a first pass though the data, trying to figure out the
236	// character set.
237
238	CharSet charSet = kCharSetUnknown;
239
240	bool isValidUTF8 = true;
241
242	bool hasEncodingMarker = false;
243
244	uint64 firstOffset = stream.Position ();
245
246	uint64 nextOffset = firstOffset;
247
248	while (nextOffset + 5 < stream.Length ())
249		{
250
251		stream.SetReadPosition (nextOffset);
252
253		uint8 firstByte = stream.Get_uint8 ();
254
255		if (firstByte != 0x1C) break;
256
257		uint8  record   = stream.Get_uint8  ();
258		uint8  dataSet  = stream.Get_uint8  ();
259		uint32 dataSize = stream.Get_uint16 ();
260
261		nextOffset = stream.Position () + dataSize;
262
263		if (record == 1)
264			{
265
266			switch (dataSet)
267				{
268
269				case 90:
270					{
271
272					hasEncodingMarker = true;
273
274					if (dataSize == 3)
275						{
276
277						uint32 byte1 = stream.Get_uint8 ();
278						uint32 byte2 = stream.Get_uint8 ();
279						uint32 byte3 = stream.Get_uint8 ();
280
281						if (byte1 == 27 /* Escape */ &&
282							byte2 == 0x25 &&
283							byte3 == 0x47)
284							{
285
286							charSet = kCharSetUTF8;
287
288							}
289
290						}
291
292					break;
293
294					}
295
296				default:
297					break;
298
299				}
300
301			}
302
303		else if (record == 2)
304			{
305
306			dng_memory_data buffer (dataSize + 1);
307
308			char *s = buffer.Buffer_char ();
309
310			stream.Get (s, dataSize);
311
312			s [dataSize] = 0;
313
314			isValidUTF8 = isValidUTF8 && dng_string::IsUTF8 (s);
315
316			}
317
318		}
319
320	// If we don't have an encoding marker, and the data is valid
321	// UTF-8, then assume that it is UTF-8 (rather than system encoding).
322
323	if (!hasEncodingMarker && isValidUTF8)
324		{
325
326		charSet = kCharSetUTF8;
327
328		}
329
330	// Make a second pass though the data, actually reading the data.
331
332	nextOffset = firstOffset;
333
334	while (nextOffset + 5 < stream.Length ())
335		{
336
337		stream.SetReadPosition (nextOffset);
338
339		uint8 firstByte = stream.Get_uint8 ();
340
341		if (firstByte != 0x1C) break;
342
343		uint8  record   = stream.Get_uint8  ();
344		uint8  dataSet  = stream.Get_uint8  ();
345		uint32 dataSize = stream.Get_uint16 ();
346
347		nextOffset = stream.Position () + dataSize;
348
349		if (record == 2)
350			{
351
352			stream.SetReadPosition (stream.Position () - 2);
353
354			switch ((DataSet) dataSet)
355				{
356
357				case kObjectNameSet:
358					{
359					ParseString (stream, fTitle, charSet);
360					break;
361					}
362
363				case kUrgencySet:
364					{
365
366					int32 size = stream.Get_uint16 ();
367
368					if (size == 1)
369						{
370
371						char c = stream.Get_int8 ();
372
373						if (c >= '0' && c <= '9')
374							{
375							fUrgency = c - '0';
376							}
377
378						}
379
380					break;
381
382					}
383
384				case kCategorySet:
385					{
386					ParseString (stream, fCategory, charSet);
387					break;
388					}
389
390				case kSupplementalCategoriesSet:
391					{
392
393					dng_string category;
394
395					ParseString (stream, category, charSet);
396
397					if (category.NotEmpty ())
398						{
399						fSupplementalCategories.Append (category);
400						}
401
402					break;
403
404					}
405
406				case kKeywordsSet:
407					{
408
409					dng_string keyword;
410
411					ParseString (stream, keyword, charSet);
412
413					if (keyword.NotEmpty ())
414						{
415						fKeywords.Append (keyword);
416						}
417
418					break;
419
420					}
421
422				case kSpecialInstructionsSet:
423					{
424					ParseString (stream, fInstructions, charSet);
425					break;
426					}
427
428				case kDateCreatedSet:
429					{
430
431					uint32 length = stream.Get_uint16 ();
432
433					if (length == 8)
434						{
435
436						char date [9];
437
438						stream.Get (date, 8);
439
440						date [8] = 0;
441
442						fDateTimeCreated.Decode_IPTC_Date (date);
443
444						}
445
446					break;
447
448					}
449
450				case kTimeCreatedSet:
451					{
452
453					uint32 length = stream.Get_uint16 ();
454
455					if (length >= 4 && length <= 11)
456						{
457
458						char time [12];
459
460						stream.Get (time, length);
461
462						time [length] = 0;
463
464						fDateTimeCreated.Decode_IPTC_Time (time);
465
466						}
467
468					break;
469
470					}
471
472				case kDigitalCreationDateSet:
473					{
474
475					uint32 length = stream.Get_uint16 ();
476
477					if (length == 8)
478						{
479
480						char date [9];
481
482						stream.Get (date, 8);
483
484						date [8] = 0;
485
486						fDigitalCreationDateTime.Decode_IPTC_Date (date);
487
488						}
489
490					break;
491
492					}
493
494				case kDigitalCreationTimeSet:
495					{
496
497					uint32 length = stream.Get_uint16 ();
498
499					if (length >= 4 && length <= 11)
500						{
501
502						char time [12];
503
504						stream.Get (time, length);
505
506						time [length] = 0;
507
508						fDigitalCreationDateTime.Decode_IPTC_Time (time);
509
510						}
511
512					break;
513
514					}
515
516				case kBylineSet:
517					{
518
519					dng_string author;
520
521					ParseString (stream, author, charSet);
522
523					if (author.NotEmpty ())
524						{
525						fAuthors.Append (author);
526						}
527
528					break;
529
530					}
531
532				case kBylineTitleSet:
533					{
534					ParseString (stream, fAuthorsPosition, charSet);
535					break;
536					}
537
538				case kCitySet:
539					{
540					ParseString (stream, fCity, charSet);
541					break;
542					}
543
544				case kProvinceStateSet:
545					{
546					ParseString (stream, fState, charSet);
547					break;
548					}
549
550				case kCountryNameSet:
551					{
552					ParseString (stream, fCountry, charSet);
553					break;
554					}
555
556				case kCountryCodeSet:
557					{
558					ParseString (stream, fCountryCode, charSet);
559					break;
560					}
561
562				case kSublocationSet:
563					{
564					ParseString (stream, fLocation, charSet);
565					break;
566					}
567
568				case kOriginalTransmissionReferenceSet:
569					{
570					ParseString (stream, fTransmissionReference, charSet);
571					break;
572					}
573
574				case kHeadlineSet:
575					{
576					ParseString (stream, fHeadline, charSet);
577					break;
578					}
579
580				case kCreditSet:
581					{
582					ParseString (stream, fCredit, charSet);
583					break;
584					}
585
586				case kSourceSet:
587					{
588					ParseString (stream, fSource, charSet);
589					break;
590					}
591
592				case kCopyrightNoticeSet:
593					{
594					ParseString (stream, fCopyrightNotice, charSet);
595					break;
596					}
597
598				case kCaptionSet:
599					{
600					ParseString (stream, fDescription, charSet);
601					break;
602					}
603
604				case kCaptionWriterSet:
605					{
606					ParseString (stream, fDescriptionWriter, charSet);
607					break;
608					}
609
610				// All other IPTC records are not part of the IPTC core
611				// and/or are not kept in sync with XMP tags, so we ignore
612				// them.
613
614				default:
615					break;
616
617				}
618
619			}
620
621		}
622
623	}
624
625/*****************************************************************************/
626
627void dng_iptc::SpoolString (dng_stream &stream,
628							const dng_string &s,
629							uint8 dataSet,
630							uint32 maxChars,
631							CharSet charSet)
632	{
633
634	if (s.IsEmpty ())
635		{
636		return;
637		}
638
639	stream.Put_uint16 (0x1C02);
640	stream.Put_uint8  (dataSet);
641
642	dng_string ss (s);
643
644	ss.SetLineEndingsToReturns ();
645
646	if (charSet == kCharSetUTF8)
647		{
648
649		// UTF-8 encoding.
650
651		if (ss.Length () > maxChars)
652			{
653			ss.Truncate (maxChars);
654			}
655
656		uint32 len = ss.Length ();
657
658		stream.Put_uint16 ((uint16) len);
659
660		stream.Put (ss.Get (), len);
661
662		}
663
664	else
665		{
666
667		// System character set encoding.
668
669		dng_memory_data buffer;
670
671		uint32 len = ss.Get_SystemEncoding (buffer);
672
673		if (len > maxChars)
674			{
675
676			uint32 lower = 0;
677			uint32 upper = ss.Length () - 1;
678
679			while (upper > lower)
680				{
681
682				uint32 middle = (upper + lower + 1) >> 1;
683
684				dng_string sss (ss);
685
686				sss.Truncate (middle);
687
688				len = sss.Get_SystemEncoding (buffer);
689
690				if (len <= maxChars)
691					{
692
693					lower = middle;
694
695					}
696
697				else
698					{
699
700					upper = middle - 1;
701
702					}
703
704				}
705
706			ss.Truncate (lower);
707
708			len = ss.Get_SystemEncoding (buffer);
709
710			}
711
712		stream.Put_uint16 ((uint16) len);
713
714		stream.Put (buffer.Buffer_char (), len);
715
716		}
717
718	}
719/*****************************************************************************/
720
721dng_memory_block * dng_iptc::Spool (dng_memory_allocator &allocator,
722									bool padForTIFF)
723	{
724
725	uint32 j;
726
727	char s [64];
728
729	dng_memory_stream stream (allocator, NULL, 2048);
730
731	stream.SetBigEndian ();
732
733	// Medata working group - now we just always write UTF-8.
734
735	CharSet charSet = kCharSetUTF8;
736
737	// UTF-8 encoding marker.
738
739	if (charSet == kCharSetUTF8)
740		{
741
742		stream.Put_uint16 (0x1C01);
743		stream.Put_uint8  (90);
744		stream.Put_uint16 (3);
745		stream.Put_uint8  (27);
746		stream.Put_uint8  (0x25);
747		stream.Put_uint8  (0x47);
748
749		}
750
751	stream.Put_uint16 (0x1C02);
752	stream.Put_uint8  (kRecordVersionSet);
753	stream.Put_uint16 (2);
754	stream.Put_uint16 (4);
755
756	SpoolString (stream,
757				 fTitle,
758				 kObjectNameSet,
759				 64,
760				 charSet);
761
762	if (fUrgency >= 0)
763		{
764
765		sprintf (s, "%1u", (unsigned) fUrgency);
766
767		stream.Put_uint16 (0x1C02);
768		stream.Put_uint8  (kUrgencySet);
769
770		stream.Put_uint16 (1);
771
772		stream.Put (s, 1);
773
774		}
775
776	SpoolString (stream,
777				 fCategory,
778				 kCategorySet,
779				 3,
780				 charSet);
781
782	for (j = 0; j < fSupplementalCategories.Count (); j++)
783		{
784
785		SpoolString (stream,
786				 	 fSupplementalCategories [j],
787				     kSupplementalCategoriesSet,
788				     32,
789					 charSet);
790
791		}
792
793	for (j = 0; j < fKeywords.Count (); j++)
794		{
795
796		SpoolString (stream,
797				 	 fKeywords [j],
798				     kKeywordsSet,
799				     64,
800					 charSet);
801
802		}
803
804	SpoolString (stream,
805				 fInstructions,
806				 kSpecialInstructionsSet,
807				 255,
808				 charSet);
809
810	if (fDateTimeCreated.IsValid ())
811		{
812
813		dng_string dateString = fDateTimeCreated.Encode_IPTC_Date ();
814
815		if (dateString.NotEmpty ())
816			{
817
818			DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date");
819
820			stream.Put_uint16 (0x1C02);
821			stream.Put_uint8  (kDateCreatedSet);
822
823			stream.Put_uint16 (8);
824
825			stream.Put (dateString.Get (), 8);
826
827			}
828
829		dng_string timeString = fDateTimeCreated.Encode_IPTC_Time ();
830
831		if (timeString.NotEmpty ())
832			{
833
834			stream.Put_uint16 (0x1C02);
835			stream.Put_uint8  (kTimeCreatedSet);
836
837			stream.Put_uint16 ((uint16)timeString.Length ());
838
839			stream.Put (timeString.Get (), timeString.Length ());
840
841			}
842
843		}
844
845	if (fDigitalCreationDateTime.IsValid ())
846		{
847
848		dng_string dateString = fDigitalCreationDateTime.Encode_IPTC_Date ();
849
850		if (dateString.NotEmpty ())
851			{
852
853			DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date");
854
855			stream.Put_uint16 (0x1C02);
856			stream.Put_uint8  (kDigitalCreationDateSet);
857
858			stream.Put_uint16 (8);
859
860			stream.Put (dateString.Get (), 8);
861
862			}
863
864		dng_string timeString = fDigitalCreationDateTime.Encode_IPTC_Time ();
865
866		if (timeString.NotEmpty ())
867			{
868
869			stream.Put_uint16 (0x1C02);
870			stream.Put_uint8  (kDigitalCreationTimeSet);
871
872			stream.Put_uint16 ((uint16)timeString.Length ());
873
874			stream.Put (timeString.Get (), timeString.Length ());
875
876			}
877
878		}
879
880	for (j = 0; j < fAuthors.Count (); j++)
881		{
882
883		SpoolString (stream,
884					 fAuthors [j],
885					 kBylineSet,
886					 32,
887					 charSet);
888
889		}
890
891	SpoolString (stream,
892				 fAuthorsPosition,
893				 kBylineTitleSet,
894				 32,
895				 charSet);
896
897	SpoolString (stream,
898				 fCity,
899				 kCitySet,
900				 32,
901				 charSet);
902
903	SpoolString (stream,
904				 fLocation,
905				 kSublocationSet,
906				 32,
907				 charSet);
908
909	SpoolString (stream,
910				 fState,
911				 kProvinceStateSet,
912				 32,
913				 charSet);
914
915	SpoolString (stream,
916				 fCountryCode,
917				 kCountryCodeSet,
918				 3,
919				 charSet);
920
921	SpoolString (stream,
922				 fCountry,
923				 kCountryNameSet,
924				 64,
925				 charSet);
926
927	SpoolString (stream,
928				 fTransmissionReference,
929				 kOriginalTransmissionReferenceSet,
930				 32,
931				 charSet);
932
933	SpoolString (stream,
934				 fHeadline,
935				 kHeadlineSet,
936				 255,
937				 charSet);
938
939	SpoolString (stream,
940				 fCredit,
941				 kCreditSet,
942				 32,
943				 charSet);
944
945	SpoolString (stream,
946				 fSource,
947				 kSourceSet,
948				 32,
949				 charSet);
950
951	SpoolString (stream,
952				 fCopyrightNotice,
953				 kCopyrightNoticeSet,
954				 128,
955				 charSet);
956
957	SpoolString (stream,
958				 fDescription,
959				 kCaptionSet,
960				 2000,
961				 charSet);
962
963	SpoolString (stream,
964				 fDescriptionWriter,
965				 kCaptionWriterSet,
966				 32,
967				 charSet);
968
969	if (padForTIFF)
970		{
971
972		while (stream.Length () & 3)
973			{
974			stream.Put_uint8 (0);
975			}
976
977		}
978
979	stream.Flush ();
980
981	return stream.AsMemoryBlock (allocator);
982
983	}
984
985/*****************************************************************************/
986