• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
dng_iptc()26 dng_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 
~dng_iptc()73 dng_iptc::~dng_iptc ()
74 	{
75 
76 	}
77 
78 /*****************************************************************************/
79 
IsEmpty() const80 bool 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 
ParseString(dng_stream & stream,dng_string & s,CharSet charSet)183 void 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 
Parse(const void * blockData,uint32 blockSize,uint64 offsetInOriginalFile)224 void 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 
SpoolString(dng_stream & stream,const dng_string & s,uint8 dataSet,uint32 maxChars,CharSet charSet)627 void 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 
Spool(dng_memory_allocator & allocator,bool padForTIFF)721 dng_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