1//--------------------------------------------------------------------------
2//  Process IPTC data and XMP data.
3//--------------------------------------------------------------------------
4#include "jhead.h"
5
6// IPTC entry types known to Jhead (there's many more defined)
7#define IPTC_RECORD_VERSION         0x00
8#define IPTC_SUPLEMENTAL_CATEGORIES 0x14
9#define IPTC_KEYWORDS               0x19
10#define IPTC_CAPTION                0x78
11#define IPTC_AUTHOR                 0x7A
12#define IPTC_HEADLINE               0x69
13#define IPTC_SPECIAL_INSTRUCTIONS   0x28
14#define IPTC_CATEGORY               0x0F
15#define IPTC_BYLINE                 0x50
16#define IPTC_BYLINE_TITLE           0x55
17#define IPTC_CREDIT                 0x6E
18#define IPTC_SOURCE                 0x73
19#define IPTC_COPYRIGHT_NOTICE       0x74
20#define IPTC_OBJECT_NAME            0x05
21#define IPTC_CITY                   0x5A
22#define IPTC_STATE                  0x5F
23#define IPTC_COUNTRY                0x65
24#define IPTC_TRANSMISSION_REFERENCE 0x67
25#define IPTC_DATE                   0x37
26#define IPTC_COPYRIGHT              0x0A
27#define IPTC_COUNTRY_CODE           0x64
28#define IPTC_REFERENCE_SERVICE      0x2D
29#define IPTC_TIME_CREATED           0x3C
30#define IPTC_SUB_LOCATION           0x5C
31#define IPTC_IMAGE_TYPE             0x82
32
33//--------------------------------------------------------------------------
34//  Process and display IPTC marker.
35//
36//  IPTC block consists of:
37//      - Marker:               1 byte      (0xED)
38//      - Block length:         2 bytes
39//      - IPTC Signature:       14 bytes    ("Photoshop 3.0\0")
40//      - 8BIM Signature        4 bytes     ("8BIM")
41//      - IPTC Block start      2 bytes     (0x04, 0x04)
42//      - IPTC Header length    1 byte
43//      - IPTC header           Header is padded to even length, counting the length byte
44//      - Length                4 bytes
45//      - IPTC Data which consists of a number of entries, each of which has the following format:
46//              - Signature     2 bytes     (0x1C02)
47//              - Entry type    1 byte      (for defined entry types, see #defines above)
48//              - entry length  2 bytes
49//              - entry data    'entry length' bytes
50//
51//--------------------------------------------------------------------------
52void show_IPTC (unsigned char* Data, unsigned int itemlen)
53{
54    const char IptcSig1[] = "Photoshop 3.0";
55    const char IptcSig2[] = "8BIM";
56    const char IptcSig3[] = {0x04, 0x04};
57
58    unsigned char * pos    = Data + sizeof(short);   // position data pointer after length field
59    unsigned char * maxpos = Data+itemlen;
60    char  headerLen = 0;
61
62    if (itemlen < 25) goto corrupt;
63
64    // Check IPTC signatures
65    if (memcmp(pos, IptcSig1, sizeof(IptcSig1)-1) != 0) goto badsig;
66    pos += sizeof(IptcSig1);      // move data pointer to the next field
67
68    if (memcmp(pos, IptcSig2, sizeof(IptcSig2)-1) != 0) goto badsig;
69    pos += sizeof(IptcSig2)-1;          // move data pointer to the next field
70
71    if (memcmp(pos, IptcSig3, sizeof(IptcSig3)) != 0){
72badsig:
73        if (ShowTags){
74            ErrNonfatal("IPTC type signature mismatch\n",0,0);
75        }
76        return;
77    }
78    pos += sizeof(IptcSig3);          // move data pointer to the next field
79
80    if (pos >= maxpos) goto corrupt;
81
82    // IPTC section found
83
84    // Skip header
85    headerLen = *pos++;                     // get header length and move data pointer to the next field
86    pos += headerLen + 1 - (headerLen % 2); // move data pointer to the next field (Header is padded to even length, counting the length byte)
87
88    if (pos+4 >= maxpos) goto corrupt;
89
90    // Get length (from motorola format)
91    //length = (*pos << 24) | (*(pos+1) << 16) | (*(pos+2) << 8) | *(pos+3);
92
93    pos += 4;                    // move data pointer to the next field
94
95    printf("======= IPTC data: =======\n");
96
97    // Now read IPTC data
98    while (pos < (Data + itemlen-5)) {
99        short  signature;
100        unsigned char   type = 0;
101        short  length = 0;
102        char * description = NULL;
103
104        if (pos+5 > maxpos) goto corrupt;
105
106        signature = (*pos << 8) + (*(pos+1));
107        pos += 2;
108
109        if (signature != 0x1C02){
110            break;
111        }
112
113        type    = *pos++;
114        length  = (*pos << 8) + (*(pos+1));
115        pos    += 2;                          // Skip tag length
116
117        if (pos+length > maxpos) goto corrupt;
118        // Process tag here
119        switch (type) {
120            case IPTC_RECORD_VERSION:
121                printf("Record vers.  : %d\n", (*pos << 8) + (*(pos+1)));
122                break;
123
124            case IPTC_SUPLEMENTAL_CATEGORIES:  description = "SuplementalCategories"; break;
125            case IPTC_KEYWORDS:                description = "Keywords"; break;
126            case IPTC_CAPTION:                 description = "Caption"; break;
127            case IPTC_AUTHOR:                  description = "Author"; break;
128            case IPTC_HEADLINE:                description = "Headline"; break;
129            case IPTC_SPECIAL_INSTRUCTIONS:    description = "Spec. Instr."; break;
130            case IPTC_CATEGORY:                description = "Category"; break;
131            case IPTC_BYLINE:                  description = "Byline"; break;
132            case IPTC_BYLINE_TITLE:            description = "Byline Title"; break;
133            case IPTC_CREDIT:                  description = "Credit"; break;
134            case IPTC_SOURCE:                  description = "Source"; break;
135            case IPTC_COPYRIGHT_NOTICE:        description = "(C)Notice"; break;
136            case IPTC_OBJECT_NAME:             description = "Object Name"; break;
137            case IPTC_CITY:                    description = "City"; break;
138            case IPTC_STATE:                   description = "State"; break;
139            case IPTC_COUNTRY:                 description = "Country"; break;
140            case IPTC_TRANSMISSION_REFERENCE:  description = "OriginalTransmissionReference"; break;
141            case IPTC_DATE:                    description = "DateCreated"; break;
142            case IPTC_COPYRIGHT:               description = "(C)Flag"; break;
143            case IPTC_REFERENCE_SERVICE:       description = "Country Code"; break;
144            case IPTC_COUNTRY_CODE:            description = "Ref. Service"; break;
145            case IPTC_TIME_CREATED:            description = "Time Created"; break;
146            case IPTC_SUB_LOCATION:            description = "Sub Location"; break;
147            case IPTC_IMAGE_TYPE:              description = "Image type"; break;
148
149            default:
150                if (ShowTags){
151                    printf("Unrecognised IPTC tag: %d\n", type );
152                }
153            break;
154        }
155        if (description != NULL) {
156            char TempBuf[32];
157            memset(TempBuf, 0, sizeof(TempBuf));
158            memset(TempBuf, ' ', 14);
159            memcpy(TempBuf, description, strlen(description));
160            strcat(TempBuf, ":");
161            printf("%s %*.*s\n", TempBuf, length, length, pos);
162        }
163        pos += length;
164    }
165    return;
166corrupt:
167    ErrNonfatal("Pointer corruption in IPTC\n",0,0);
168}
169
170
171
172//--------------------------------------------------------------------------
173// Dump contents of XMP section
174//--------------------------------------------------------------------------
175void ShowXmp(Section_t XmpSection)
176{
177    unsigned char * Data;
178    char OutLine[101];
179    int OutLineChars;
180    int NonBlank;
181    unsigned a;
182    NonBlank = 0;
183    Data = XmpSection.Data;
184    OutLineChars = 0;
185
186
187    for (a=0;a<XmpSection.Size;a++){
188        if (Data[a] >= 32 && Data[a] < 128){
189            OutLine[OutLineChars++] = Data[a];
190            if (Data[a] != ' ') NonBlank |= 1;
191        }else{
192            if (Data[a] != '\n'){
193                OutLine[OutLineChars++] = '?';
194            }
195        }
196        if (Data[a] == '\n' || OutLineChars >= 100){
197            OutLine[OutLineChars] = 0;
198            if (NonBlank){
199                puts(OutLine);
200            }
201            NonBlank = (NonBlank & 1) << 1;
202            OutLineChars = 0;
203        }
204    }
205}
206