| //-------------------------------------------------------------------------- |
| // Process IPTC data and XMP data. |
| //-------------------------------------------------------------------------- |
| #include "jhead.h" |
| |
| // IPTC entry types known to Jhead (there's many more defined) |
| #define IPTC_RECORD_VERSION 0x00 |
| #define IPTC_SUPLEMENTAL_CATEGORIES 0x14 |
| #define IPTC_KEYWORDS 0x19 |
| #define IPTC_CAPTION 0x78 |
| #define IPTC_AUTHOR 0x7A |
| #define IPTC_HEADLINE 0x69 |
| #define IPTC_SPECIAL_INSTRUCTIONS 0x28 |
| #define IPTC_CATEGORY 0x0F |
| #define IPTC_BYLINE 0x50 |
| #define IPTC_BYLINE_TITLE 0x55 |
| #define IPTC_CREDIT 0x6E |
| #define IPTC_SOURCE 0x73 |
| #define IPTC_COPYRIGHT_NOTICE 0x74 |
| #define IPTC_OBJECT_NAME 0x05 |
| #define IPTC_CITY 0x5A |
| #define IPTC_STATE 0x5F |
| #define IPTC_COUNTRY 0x65 |
| #define IPTC_TRANSMISSION_REFERENCE 0x67 |
| #define IPTC_DATE 0x37 |
| #define IPTC_COPYRIGHT 0x0A |
| #define IPTC_COUNTRY_CODE 0x64 |
| #define IPTC_REFERENCE_SERVICE 0x2D |
| #define IPTC_TIME_CREATED 0x3C |
| #define IPTC_SUB_LOCATION 0x5C |
| #define IPTC_IMAGE_TYPE 0x82 |
| |
| //-------------------------------------------------------------------------- |
| // Process and display IPTC marker. |
| // |
| // IPTC block consists of: |
| // - Marker: 1 byte (0xED) |
| // - Block length: 2 bytes |
| // - IPTC Signature: 14 bytes ("Photoshop 3.0\0") |
| // - 8BIM Signature 4 bytes ("8BIM") |
| // - IPTC Block start 2 bytes (0x04, 0x04) |
| // - IPTC Header length 1 byte |
| // - IPTC header Header is padded to even length, counting the length byte |
| // - Length 4 bytes |
| // - IPTC Data which consists of a number of entries, each of which has the following format: |
| // - Signature 2 bytes (0x1C02) |
| // - Entry type 1 byte (for defined entry types, see #defines above) |
| // - entry length 2 bytes |
| // - entry data 'entry length' bytes |
| // |
| //-------------------------------------------------------------------------- |
| void show_IPTC (unsigned char* Data, unsigned int itemlen) |
| { |
| const char IptcSig1[] = "Photoshop 3.0"; |
| const char IptcSig2[] = "8BIM"; |
| const char IptcSig3[] = {0x04, 0x04}; |
| |
| unsigned char * pos = Data + sizeof(short); // position data pointer after length field |
| unsigned char * maxpos = Data+itemlen; |
| char headerLen = 0; |
| |
| if (itemlen < 25) goto corrupt; |
| |
| // Check IPTC signatures |
| if (memcmp(pos, IptcSig1, sizeof(IptcSig1)-1) != 0) goto badsig; |
| pos += sizeof(IptcSig1); // move data pointer to the next field |
| |
| if (memcmp(pos, IptcSig2, sizeof(IptcSig2)-1) != 0) goto badsig; |
| pos += sizeof(IptcSig2)-1; // move data pointer to the next field |
| |
| if (memcmp(pos, IptcSig3, sizeof(IptcSig3)) != 0){ |
| badsig: |
| if (ShowTags){ |
| ErrNonfatal("IPTC type signature mismatch\n",0,0); |
| } |
| return; |
| } |
| pos += sizeof(IptcSig3); // move data pointer to the next field |
| |
| if (pos >= maxpos) goto corrupt; |
| |
| // IPTC section found |
| |
| // Skip header |
| headerLen = *pos++; // get header length and move data pointer to the next field |
| pos += headerLen + 1 - (headerLen % 2); // move data pointer to the next field (Header is padded to even length, counting the length byte) |
| |
| if (pos+4 >= maxpos) goto corrupt; |
| |
| // Get length (from motorola format) |
| //length = (*pos << 24) | (*(pos+1) << 16) | (*(pos+2) << 8) | *(pos+3); |
| |
| pos += 4; // move data pointer to the next field |
| |
| printf("======= IPTC data: =======\n"); |
| |
| // Now read IPTC data |
| while (pos < (Data + itemlen-5)) { |
| short signature; |
| unsigned char type = 0; |
| short length = 0; |
| char * description = NULL; |
| |
| if (pos+5 > maxpos) goto corrupt; |
| |
| signature = (*pos << 8) + (*(pos+1)); |
| pos += 2; |
| |
| if (signature != 0x1C02){ |
| break; |
| } |
| |
| type = *pos++; |
| length = (*pos << 8) + (*(pos+1)); |
| pos += 2; // Skip tag length |
| |
| if (pos+length > maxpos) goto corrupt; |
| // Process tag here |
| switch (type) { |
| case IPTC_RECORD_VERSION: |
| printf("Record vers. : %d\n", (*pos << 8) + (*(pos+1))); |
| break; |
| |
| case IPTC_SUPLEMENTAL_CATEGORIES: description = "SuplementalCategories"; break; |
| case IPTC_KEYWORDS: description = "Keywords"; break; |
| case IPTC_CAPTION: description = "Caption"; break; |
| case IPTC_AUTHOR: description = "Author"; break; |
| case IPTC_HEADLINE: description = "Headline"; break; |
| case IPTC_SPECIAL_INSTRUCTIONS: description = "Spec. Instr."; break; |
| case IPTC_CATEGORY: description = "Category"; break; |
| case IPTC_BYLINE: description = "Byline"; break; |
| case IPTC_BYLINE_TITLE: description = "Byline Title"; break; |
| case IPTC_CREDIT: description = "Credit"; break; |
| case IPTC_SOURCE: description = "Source"; break; |
| case IPTC_COPYRIGHT_NOTICE: description = "(C)Notice"; break; |
| case IPTC_OBJECT_NAME: description = "Object Name"; break; |
| case IPTC_CITY: description = "City"; break; |
| case IPTC_STATE: description = "State"; break; |
| case IPTC_COUNTRY: description = "Country"; break; |
| case IPTC_TRANSMISSION_REFERENCE: description = "OriginalTransmissionReference"; break; |
| case IPTC_DATE: description = "DateCreated"; break; |
| case IPTC_COPYRIGHT: description = "(C)Flag"; break; |
| case IPTC_REFERENCE_SERVICE: description = "Country Code"; break; |
| case IPTC_COUNTRY_CODE: description = "Ref. Service"; break; |
| case IPTC_TIME_CREATED: description = "Time Created"; break; |
| case IPTC_SUB_LOCATION: description = "Sub Location"; break; |
| case IPTC_IMAGE_TYPE: description = "Image type"; break; |
| |
| default: |
| if (ShowTags){ |
| printf("Unrecognised IPTC tag: %d\n", type ); |
| } |
| break; |
| } |
| if (description != NULL) { |
| char TempBuf[32]; |
| memset(TempBuf, 0, sizeof(TempBuf)); |
| memset(TempBuf, ' ', 14); |
| memcpy(TempBuf, description, strlen(description)); |
| strcat(TempBuf, ":"); |
| printf("%s %*.*s\n", TempBuf, length, length, pos); |
| } |
| pos += length; |
| } |
| return; |
| corrupt: |
| ErrNonfatal("Pointer corruption in IPTC\n",0,0); |
| } |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // Dump contents of XMP section |
| //-------------------------------------------------------------------------- |
| void ShowXmp(Section_t XmpSection) |
| { |
| unsigned char * Data; |
| char OutLine[101]; |
| int OutLineChars; |
| int NonBlank; |
| unsigned a; |
| NonBlank = 0; |
| Data = XmpSection.Data; |
| OutLineChars = 0; |
| |
| |
| for (a=0;a<XmpSection.Size;a++){ |
| if (Data[a] >= 32 && Data[a] < 128){ |
| OutLine[OutLineChars++] = Data[a]; |
| if (Data[a] != ' ') NonBlank |= 1; |
| }else{ |
| if (Data[a] != '\n'){ |
| OutLine[OutLineChars++] = '?'; |
| } |
| } |
| if (Data[a] == '\n' || OutLineChars >= 100){ |
| OutLine[OutLineChars] = 0; |
| if (NonBlank){ |
| puts(OutLine); |
| } |
| NonBlank = (NonBlank & 1) << 1; |
| OutLineChars = 0; |
| } |
| } |
| } |