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 /*****************************************************************************/