File indexing completed on 2025-01-19 03:55:03
0001 /*****************************************************************************/ 0002 // Copyright 2006-2019 Adobe Systems Incorporated 0003 // All Rights Reserved. 0004 // 0005 // NOTICE: Adobe permits you to use, modify, and distribute this file in 0006 // accordance with the terms of the Adobe license agreement accompanying it. 0007 /*****************************************************************************/ 0008 0009 #include "dng_iptc.h" 0010 0011 #include "dng_assertions.h" 0012 #include "dng_auto_ptr.h" 0013 #include "dng_memory_stream.h" 0014 #include "dng_stream.h" 0015 #include "dng_utils.h" 0016 0017 /*****************************************************************************/ 0018 0019 dng_iptc::dng_iptc () 0020 0021 : fTitle () 0022 0023 , fUrgency (-1) 0024 0025 , fCategory () 0026 0027 , fSupplementalCategories () 0028 0029 , fKeywords () 0030 0031 , fInstructions () 0032 0033 , fDateTimeCreated () 0034 0035 , fDigitalCreationDateTime () 0036 0037 , fAuthors () 0038 , fAuthorsPosition () 0039 0040 , fCity () 0041 , fState () 0042 , fCountry () 0043 , fCountryCode () 0044 0045 , fLocation () 0046 0047 , fTransmissionReference () 0048 0049 , fHeadline () 0050 0051 , fCredit () 0052 0053 , fSource () 0054 0055 , fCopyrightNotice () 0056 0057 , fDescription () 0058 , fDescriptionWriter () 0059 0060 { 0061 0062 } 0063 0064 /*****************************************************************************/ 0065 0066 dng_iptc::~dng_iptc () 0067 { 0068 0069 } 0070 0071 /*****************************************************************************/ 0072 0073 bool dng_iptc::IsEmpty () const 0074 { 0075 0076 if (fTitle.NotEmpty ()) 0077 { 0078 return false; 0079 } 0080 0081 if (fUrgency >= 0) 0082 { 0083 return false; 0084 } 0085 0086 if (fCategory.NotEmpty ()) 0087 { 0088 return false; 0089 } 0090 0091 if (fSupplementalCategories.Count () > 0) 0092 { 0093 return false; 0094 } 0095 0096 if (fKeywords.Count () > 0) 0097 { 0098 return false; 0099 } 0100 0101 if (fInstructions.NotEmpty ()) 0102 { 0103 return false; 0104 } 0105 0106 if (fDateTimeCreated.IsValid ()) 0107 { 0108 return false; 0109 } 0110 0111 if (fDigitalCreationDateTime.IsValid ()) 0112 { 0113 return false; 0114 } 0115 0116 if (fAuthors.Count () != 0 || 0117 fAuthorsPosition.NotEmpty ()) 0118 { 0119 return false; 0120 } 0121 0122 if (fCity .NotEmpty () || 0123 fState .NotEmpty () || 0124 fCountry.NotEmpty ()) 0125 { 0126 return false; 0127 } 0128 0129 if (fCountryCode.NotEmpty ()) 0130 { 0131 return false; 0132 } 0133 0134 if (fLocation.NotEmpty ()) 0135 { 0136 return false; 0137 } 0138 0139 if (fTransmissionReference.NotEmpty ()) 0140 { 0141 return false; 0142 } 0143 0144 if (fHeadline.NotEmpty ()) 0145 { 0146 return false; 0147 } 0148 0149 if (fCredit.NotEmpty ()) 0150 { 0151 return false; 0152 } 0153 0154 if (fSource.NotEmpty ()) 0155 { 0156 return false; 0157 } 0158 0159 if (fCopyrightNotice.NotEmpty ()) 0160 { 0161 return false; 0162 } 0163 0164 if (fDescription .NotEmpty () || 0165 fDescriptionWriter.NotEmpty ()) 0166 { 0167 return false; 0168 } 0169 0170 return true; 0171 0172 } 0173 0174 /*****************************************************************************/ 0175 0176 void dng_iptc::ParseString (dng_stream &stream, 0177 dng_string &s, 0178 CharSet charSet) 0179 { 0180 0181 uint32 length = stream.Get_uint16 (); 0182 0183 dng_memory_data buffer (length + 1); 0184 0185 char *c = buffer.Buffer_char (); 0186 0187 stream.Get (c, length); 0188 0189 c [length] = 0; 0190 0191 switch (charSet) 0192 { 0193 0194 case kCharSetUTF8: 0195 { 0196 s.Set_UTF8 (c); 0197 break; 0198 } 0199 0200 default: 0201 { 0202 s.Set_SystemEncoding (c); 0203 } 0204 0205 } 0206 0207 s.SetLineEndingsToNewLines (); 0208 0209 s.StripLowASCII (); 0210 0211 s.TrimTrailingBlanks (); 0212 0213 } 0214 0215 /*****************************************************************************/ 0216 0217 void dng_iptc::Parse (const void *blockData, 0218 uint32 blockSize, 0219 uint64 offsetInOriginalFile) 0220 { 0221 0222 dng_stream stream (blockData, 0223 blockSize, 0224 offsetInOriginalFile); 0225 0226 stream.SetBigEndian (); 0227 0228 // Make a first pass though the data, trying to figure out the 0229 // character set. 0230 0231 CharSet charSet = kCharSetUnknown; 0232 0233 bool isValidUTF8 = true; 0234 0235 bool hasEncodingMarker = false; 0236 0237 uint64 firstOffset = stream.Position (); 0238 0239 uint64 nextOffset = firstOffset; 0240 0241 while (nextOffset + 5 < stream.Length ()) 0242 { 0243 0244 stream.SetReadPosition (nextOffset); 0245 0246 uint8 firstByte = stream.Get_uint8 (); 0247 0248 if (firstByte != 0x1C) break; 0249 0250 uint8 record = stream.Get_uint8 (); 0251 uint8 dataSet = stream.Get_uint8 (); 0252 uint32 dataSize = stream.Get_uint16 (); 0253 0254 nextOffset = stream.Position () + dataSize; 0255 0256 if (record == 1) 0257 { 0258 0259 switch (dataSet) 0260 { 0261 0262 case 90: 0263 { 0264 0265 hasEncodingMarker = true; 0266 0267 if (dataSize == 3) 0268 { 0269 0270 uint32 byte1 = stream.Get_uint8 (); 0271 uint32 byte2 = stream.Get_uint8 (); 0272 uint32 byte3 = stream.Get_uint8 (); 0273 0274 if (byte1 == 27 /* Escape */ && 0275 byte2 == 0x25 && 0276 byte3 == 0x47) 0277 { 0278 0279 charSet = kCharSetUTF8; 0280 0281 } 0282 0283 } 0284 0285 break; 0286 0287 } 0288 0289 default: 0290 break; 0291 0292 } 0293 0294 } 0295 0296 else if (record == 2) 0297 { 0298 0299 dng_memory_data buffer (dataSize + 1); 0300 0301 char *s = buffer.Buffer_char (); 0302 0303 stream.Get (s, dataSize); 0304 0305 s [dataSize] = 0; 0306 0307 isValidUTF8 = isValidUTF8 && dng_string::IsUTF8 (s); 0308 0309 } 0310 0311 } 0312 0313 // If we don't have an encoding marker, and the data is valid 0314 // UTF-8, then assume that it is UTF-8 (rather than system encoding). 0315 0316 if (!hasEncodingMarker && isValidUTF8) 0317 { 0318 0319 charSet = kCharSetUTF8; 0320 0321 } 0322 0323 // Make a second pass though the data, actually reading the data. 0324 0325 nextOffset = firstOffset; 0326 0327 while (nextOffset + 5 < stream.Length ()) 0328 { 0329 0330 stream.SetReadPosition (nextOffset); 0331 0332 uint8 firstByte = stream.Get_uint8 (); 0333 0334 if (firstByte != 0x1C) break; 0335 0336 uint8 record = stream.Get_uint8 (); 0337 uint8 dataSet = stream.Get_uint8 (); 0338 uint32 dataSize = stream.Get_uint16 (); 0339 0340 nextOffset = stream.Position () + dataSize; 0341 0342 if (record == 2) 0343 { 0344 0345 stream.SetReadPosition (stream.Position () - 2); 0346 0347 switch ((DataSet) dataSet) 0348 { 0349 0350 case kObjectNameSet: 0351 { 0352 ParseString (stream, fTitle, charSet); 0353 break; 0354 } 0355 0356 case kUrgencySet: 0357 { 0358 0359 int32 size = stream.Get_uint16 (); 0360 0361 if (size == 1) 0362 { 0363 0364 char c = stream.Get_int8 (); 0365 0366 if (c >= '0' && c <= '9') 0367 { 0368 fUrgency = c - '0'; 0369 } 0370 0371 } 0372 0373 break; 0374 0375 } 0376 0377 case kCategorySet: 0378 { 0379 ParseString (stream, fCategory, charSet); 0380 break; 0381 } 0382 0383 case kSupplementalCategoriesSet: 0384 { 0385 0386 dng_string category; 0387 0388 ParseString (stream, category, charSet); 0389 0390 if (category.NotEmpty ()) 0391 { 0392 fSupplementalCategories.Append (category); 0393 } 0394 0395 break; 0396 0397 } 0398 0399 case kKeywordsSet: 0400 { 0401 0402 dng_string keyword; 0403 0404 ParseString (stream, keyword, charSet); 0405 0406 if (keyword.NotEmpty ()) 0407 { 0408 fKeywords.Append (keyword); 0409 } 0410 0411 break; 0412 0413 } 0414 0415 case kSpecialInstructionsSet: 0416 { 0417 ParseString (stream, fInstructions, charSet); 0418 break; 0419 } 0420 0421 case kDateCreatedSet: 0422 { 0423 0424 uint32 length = stream.Get_uint16 (); 0425 0426 if (length == 8) 0427 { 0428 0429 char date [9]; 0430 0431 stream.Get (date, 8); 0432 0433 date [8] = 0; 0434 0435 fDateTimeCreated.Decode_IPTC_Date (date); 0436 0437 } 0438 0439 break; 0440 0441 } 0442 0443 case kTimeCreatedSet: 0444 { 0445 0446 uint32 length = stream.Get_uint16 (); 0447 0448 if (length >= 4 && length <= 11) 0449 { 0450 0451 char time [12]; 0452 0453 stream.Get (time, length); 0454 0455 time [length] = 0; 0456 0457 fDateTimeCreated.Decode_IPTC_Time (time); 0458 0459 } 0460 0461 break; 0462 0463 } 0464 0465 case kDigitalCreationDateSet: 0466 { 0467 0468 uint32 length = stream.Get_uint16 (); 0469 0470 if (length == 8) 0471 { 0472 0473 char date [9]; 0474 0475 stream.Get (date, 8); 0476 0477 date [8] = 0; 0478 0479 fDigitalCreationDateTime.Decode_IPTC_Date (date); 0480 0481 } 0482 0483 break; 0484 0485 } 0486 0487 case kDigitalCreationTimeSet: 0488 { 0489 0490 uint32 length = stream.Get_uint16 (); 0491 0492 if (length >= 4 && length <= 11) 0493 { 0494 0495 char time [12]; 0496 0497 stream.Get (time, length); 0498 0499 time [length] = 0; 0500 0501 fDigitalCreationDateTime.Decode_IPTC_Time (time); 0502 0503 } 0504 0505 break; 0506 0507 } 0508 0509 case kBylineSet: 0510 { 0511 0512 dng_string author; 0513 0514 ParseString (stream, author, charSet); 0515 0516 if (author.NotEmpty ()) 0517 { 0518 fAuthors.Append (author); 0519 } 0520 0521 break; 0522 0523 } 0524 0525 case kBylineTitleSet: 0526 { 0527 ParseString (stream, fAuthorsPosition, charSet); 0528 break; 0529 } 0530 0531 case kCitySet: 0532 { 0533 ParseString (stream, fCity, charSet); 0534 break; 0535 } 0536 0537 case kProvinceStateSet: 0538 { 0539 ParseString (stream, fState, charSet); 0540 break; 0541 } 0542 0543 case kCountryNameSet: 0544 { 0545 ParseString (stream, fCountry, charSet); 0546 break; 0547 } 0548 0549 case kCountryCodeSet: 0550 { 0551 ParseString (stream, fCountryCode, charSet); 0552 break; 0553 } 0554 0555 case kSublocationSet: 0556 { 0557 ParseString (stream, fLocation, charSet); 0558 break; 0559 } 0560 0561 case kOriginalTransmissionReferenceSet: 0562 { 0563 ParseString (stream, fTransmissionReference, charSet); 0564 break; 0565 } 0566 0567 case kHeadlineSet: 0568 { 0569 ParseString (stream, fHeadline, charSet); 0570 break; 0571 } 0572 0573 case kCreditSet: 0574 { 0575 ParseString (stream, fCredit, charSet); 0576 break; 0577 } 0578 0579 case kSourceSet: 0580 { 0581 ParseString (stream, fSource, charSet); 0582 break; 0583 } 0584 0585 case kCopyrightNoticeSet: 0586 { 0587 ParseString (stream, fCopyrightNotice, charSet); 0588 break; 0589 } 0590 0591 case kCaptionSet: 0592 { 0593 ParseString (stream, fDescription, charSet); 0594 break; 0595 } 0596 0597 case kCaptionWriterSet: 0598 { 0599 ParseString (stream, fDescriptionWriter, charSet); 0600 break; 0601 } 0602 0603 // All other IPTC records are not part of the IPTC core 0604 // and/or are not kept in sync with XMP tags, so we ignore 0605 // them. 0606 0607 default: 0608 break; 0609 0610 } 0611 0612 } 0613 0614 } 0615 0616 } 0617 0618 /*****************************************************************************/ 0619 0620 void dng_iptc::SpoolString (dng_stream &stream, 0621 const dng_string &s, 0622 uint8 dataSet, 0623 uint32 maxChars, 0624 CharSet charSet) 0625 { 0626 0627 if (s.IsEmpty ()) 0628 { 0629 return; 0630 } 0631 0632 stream.Put_uint16 (0x1C02); 0633 stream.Put_uint8 (dataSet); 0634 0635 dng_string ss (s); 0636 0637 ss.SetLineEndingsToReturns (); 0638 0639 if (charSet == kCharSetUTF8) 0640 { 0641 0642 // UTF-8 encoding. 0643 0644 if (ss.Length () > maxChars) 0645 { 0646 ss.Truncate (maxChars); 0647 } 0648 0649 uint32 len = ss.Length (); 0650 0651 stream.Put_uint16 ((uint16) len); 0652 0653 stream.Put (ss.Get (), len); 0654 0655 } 0656 0657 else 0658 { 0659 0660 // System character set encoding. 0661 0662 dng_memory_data buffer; 0663 0664 uint32 len = ss.Get_SystemEncoding (buffer); 0665 0666 if (len > maxChars) 0667 { 0668 0669 uint32 lower = 0; 0670 uint32 upper = ss.Length () - 1; 0671 0672 while (upper > lower) 0673 { 0674 0675 uint32 middle = (upper + lower + 1) >> 1; 0676 0677 dng_string sss (ss); 0678 0679 sss.Truncate (middle); 0680 0681 len = sss.Get_SystemEncoding (buffer); 0682 0683 if (len <= maxChars) 0684 { 0685 0686 lower = middle; 0687 0688 } 0689 0690 else 0691 { 0692 0693 upper = middle - 1; 0694 0695 } 0696 0697 } 0698 0699 ss.Truncate (lower); 0700 0701 len = ss.Get_SystemEncoding (buffer); 0702 0703 } 0704 0705 stream.Put_uint16 ((uint16) len); 0706 0707 stream.Put (buffer.Buffer_char (), len); 0708 0709 } 0710 0711 } 0712 /*****************************************************************************/ 0713 0714 dng_memory_block * dng_iptc::Spool (dng_memory_allocator &allocator, 0715 bool padForTIFF) 0716 { 0717 0718 uint32 j; 0719 0720 char s [64]; 0721 0722 dng_memory_stream stream (allocator, NULL, 2048); 0723 0724 stream.SetBigEndian (); 0725 0726 // Medata working group - now we just always write UTF-8. 0727 0728 CharSet charSet = kCharSetUTF8; 0729 0730 // UTF-8 encoding marker. 0731 0732 if (charSet == kCharSetUTF8) 0733 { 0734 0735 stream.Put_uint16 (0x1C01); 0736 stream.Put_uint8 (90); 0737 stream.Put_uint16 (3); 0738 stream.Put_uint8 (27); 0739 stream.Put_uint8 (0x25); 0740 stream.Put_uint8 (0x47); 0741 0742 } 0743 0744 stream.Put_uint16 (0x1C02); 0745 stream.Put_uint8 (kRecordVersionSet); 0746 stream.Put_uint16 (2); 0747 stream.Put_uint16 (4); 0748 0749 SpoolString (stream, 0750 fTitle, 0751 kObjectNameSet, 0752 64, 0753 charSet); 0754 0755 if (fUrgency >= 0) 0756 { 0757 0758 sprintf (s, "%1u", (unsigned) fUrgency); 0759 0760 stream.Put_uint16 (0x1C02); 0761 stream.Put_uint8 (kUrgencySet); 0762 0763 stream.Put_uint16 (1); 0764 0765 stream.Put (s, 1); 0766 0767 } 0768 0769 SpoolString (stream, 0770 fCategory, 0771 kCategorySet, 0772 3, 0773 charSet); 0774 0775 for (j = 0; j < fSupplementalCategories.Count (); j++) 0776 { 0777 0778 SpoolString (stream, 0779 fSupplementalCategories [j], 0780 kSupplementalCategoriesSet, 0781 32, 0782 charSet); 0783 0784 } 0785 0786 for (j = 0; j < fKeywords.Count (); j++) 0787 { 0788 0789 SpoolString (stream, 0790 fKeywords [j], 0791 kKeywordsSet, 0792 64, 0793 charSet); 0794 0795 } 0796 0797 SpoolString (stream, 0798 fInstructions, 0799 kSpecialInstructionsSet, 0800 255, 0801 charSet); 0802 0803 if (fDateTimeCreated.IsValid ()) 0804 { 0805 0806 dng_string dateString = fDateTimeCreated.Encode_IPTC_Date (); 0807 0808 if (dateString.NotEmpty ()) 0809 { 0810 0811 DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); 0812 0813 stream.Put_uint16 (0x1C02); 0814 stream.Put_uint8 (kDateCreatedSet); 0815 0816 stream.Put_uint16 (8); 0817 0818 stream.Put (dateString.Get (), 8); 0819 0820 } 0821 0822 dng_string timeString = fDateTimeCreated.Encode_IPTC_Time (); 0823 0824 if (timeString.NotEmpty ()) 0825 { 0826 0827 stream.Put_uint16 (0x1C02); 0828 stream.Put_uint8 (kTimeCreatedSet); 0829 0830 stream.Put_uint16 ((uint16)timeString.Length ()); 0831 0832 stream.Put (timeString.Get (), timeString.Length ()); 0833 0834 } 0835 0836 } 0837 0838 if (fDigitalCreationDateTime.IsValid ()) 0839 { 0840 0841 dng_string dateString = fDigitalCreationDateTime.Encode_IPTC_Date (); 0842 0843 if (dateString.NotEmpty ()) 0844 { 0845 0846 DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); 0847 0848 stream.Put_uint16 (0x1C02); 0849 stream.Put_uint8 (kDigitalCreationDateSet); 0850 0851 stream.Put_uint16 (8); 0852 0853 stream.Put (dateString.Get (), 8); 0854 0855 } 0856 0857 dng_string timeString = fDigitalCreationDateTime.Encode_IPTC_Time (); 0858 0859 if (timeString.NotEmpty ()) 0860 { 0861 0862 stream.Put_uint16 (0x1C02); 0863 stream.Put_uint8 (kDigitalCreationTimeSet); 0864 0865 stream.Put_uint16 ((uint16)timeString.Length ()); 0866 0867 stream.Put (timeString.Get (), timeString.Length ()); 0868 0869 } 0870 0871 } 0872 0873 for (j = 0; j < fAuthors.Count (); j++) 0874 { 0875 0876 SpoolString (stream, 0877 fAuthors [j], 0878 kBylineSet, 0879 32, 0880 charSet); 0881 0882 } 0883 0884 SpoolString (stream, 0885 fAuthorsPosition, 0886 kBylineTitleSet, 0887 32, 0888 charSet); 0889 0890 SpoolString (stream, 0891 fCity, 0892 kCitySet, 0893 32, 0894 charSet); 0895 0896 SpoolString (stream, 0897 fLocation, 0898 kSublocationSet, 0899 32, 0900 charSet); 0901 0902 SpoolString (stream, 0903 fState, 0904 kProvinceStateSet, 0905 32, 0906 charSet); 0907 0908 SpoolString (stream, 0909 fCountryCode, 0910 kCountryCodeSet, 0911 3, 0912 charSet); 0913 0914 SpoolString (stream, 0915 fCountry, 0916 kCountryNameSet, 0917 64, 0918 charSet); 0919 0920 SpoolString (stream, 0921 fTransmissionReference, 0922 kOriginalTransmissionReferenceSet, 0923 32, 0924 charSet); 0925 0926 SpoolString (stream, 0927 fHeadline, 0928 kHeadlineSet, 0929 255, 0930 charSet); 0931 0932 SpoolString (stream, 0933 fCredit, 0934 kCreditSet, 0935 32, 0936 charSet); 0937 0938 SpoolString (stream, 0939 fSource, 0940 kSourceSet, 0941 32, 0942 charSet); 0943 0944 SpoolString (stream, 0945 fCopyrightNotice, 0946 kCopyrightNoticeSet, 0947 128, 0948 charSet); 0949 0950 SpoolString (stream, 0951 fDescription, 0952 kCaptionSet, 0953 2000, 0954 charSet); 0955 0956 SpoolString (stream, 0957 fDescriptionWriter, 0958 kCaptionWriterSet, 0959 32, 0960 charSet); 0961 0962 if (padForTIFF) 0963 { 0964 0965 while (stream.Length () & 3) 0966 { 0967 stream.Put_uint8 (0); 0968 } 0969 0970 } 0971 0972 stream.Flush (); 0973 0974 return stream.AsMemoryBlock (allocator); 0975 0976 } 0977 0978 /*****************************************************************************/