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