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 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 73 dng_iptc::~dng_iptc () 74 { 75 76 } 77 78 /*****************************************************************************/ 79 80 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 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 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 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 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