Skip to content

Commit 0f81385

Browse files
committed
fix: IPTC fields have length limits (#4568)
It escaped our notice before that the IPTC spec dictates length limits for many fields. Getting this wrong can confuse some software, including Photoshop, apparently. This patch enforces length limits wherever we could figure them out from the IPTC spec -- it will simply truncate those strings that are too long before writing them to a binary IPTC tag. For this reason, we also are ending the practice of automatically translating several IPTC fields to and from what we figured were the equivalent generic metadata names. This was perhaps a dubious practice to begin with, but now the enforcement of length limits for IPTC (but not the generic metadata) makes it even more frought. There is probably nobody depending on this behavior (and maybe few OIIO users depending on IPTC support at all?), so now anybody purposely using IPTC metadata is fully responsible for setting it and dealing with any issues of whether it's "out of sync" with any other metadata that OIIO stores in or reads from a file. Fixes #4342 --------- Signed-off-by: Larry Gritz <[email protected]>
1 parent 2922314 commit 0f81385

File tree

3 files changed

+111
-74
lines changed

3 files changed

+111
-74
lines changed

src/doc/stdmetadata.rst

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -916,23 +916,8 @@ conventions for storing image metadata, and this standard is growing in
916916
popularity and is commonly used in photo-browsing programs to record
917917
captions and keywords.
918918

919-
The following IPTC metadata items correspond exactly to metadata in the
920-
OpenImageIO conventions, so it is recommended that you use the standards and
921-
that plugins supporting IPTC metadata respond likewise:
922-
923-
=============== =========================================================================================================
924-
IPTC tag OpenImageIO metadata convention
925-
=============== =========================================================================================================
926-
Caption `"ImageDescription"`
927-
Keyword IPTC keywords should be concatenated, separated by semicolons (`;`), and stored as the `Keywords` attribute.
928-
ExposureTime `ExposureTime`
929-
CopyrightNotice `Copyright`
930-
Creator `Artist`
931-
=============== =========================================================================================================
932-
933-
934-
The remainder of IPTC metadata fields should use the following names,
935-
prefixed with `IPTC:` to avoid conflicts with other plugins or standards.
919+
IPTC metadata fields should use the following names, prefixed with `IPTC:` to
920+
avoid conflicts with other plugins or standards.
936921

937922
.. option:: "IPTC:ObjecTypeReference" : string
938923

@@ -958,6 +943,10 @@ prefixed with `IPTC:` to avoid conflicts with other plugins or standards.
958943

959944
Category.
960945

946+
.. option:: "IPTC:Keywords" : string
947+
948+
Semicolon-separated keywords describing the contents of the image.
949+
961950
.. option:: "IPTC:ContentLocationCode" : string
962951

963952
Code for content location.
@@ -976,6 +965,10 @@ prefixed with `IPTC:` to avoid conflicts with other plugins or standards.
976965

977966
Expiration date and time.
978967

968+
.. option:: "IPTC:ExposureTime" : string
969+
970+
The exposure time (in seconds) of the captured image.
971+
979972
.. option:: "IPTC:Instructions" : string
980973

981974
Special instructions for handling the image.
@@ -1000,6 +993,10 @@ prefixed with `IPTC:` to avoid conflicts with other plugins or standards.
1000993

1001994
The version number of the creation software.
1002995

996+
.. option:: "IPTC:Creator" : string
997+
998+
The artist, creator, or owner of the image.
999+
10031000
.. option:: "IPTC:AuthorsPosition" : string
10041001

10051002
The job title or position of the creator of the image.
@@ -1025,11 +1022,19 @@ prefixed with `IPTC:` to avoid conflicts with other plugins or standards.
10251022

10261023
The source of the image.
10271024

1025+
.. option:: "IPTC:CopyrightNotice" : string
1026+
1027+
Any copyright notice for the image.
1028+
10281029
.. option:: "IPTC:Contact" : string
10291030

10301031
The contact information for the image (possibly including name, address,
10311032
email, etc.).
10321033

1034+
.. option:: "IPTC:Caption" : string
1035+
1036+
A caption for the image.
1037+
10331038
.. option:: "IPTC:CaptionWriter" : string
10341039

10351040
The name of the person who wrote the caption or description of the
@@ -1058,6 +1063,16 @@ prefixed with `IPTC:` to avoid conflicts with other plugins or standards.
10581063
The history of the image or document.
10591064

10601065

1066+
References for more information on IPTC metadata:
1067+
1068+
* https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata
1069+
* https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#iptc-core-schema-1-5-specifications
1070+
This is the one where you can find the length limits
1071+
* ExifTool's documentation about IPTC tags (caveat: not a definitive
1072+
reference, could be outdated or incorrect):
1073+
https://exiftool.org/TagNames/IPTC.html
1074+
1075+
10611076
SMPTE metadata
10621077
==============
10631078

src/libOpenImageIO/iptc.cpp

Lines changed: 79 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,68 +21,78 @@ struct IIMtag {
2121
const char* name; // Attribute name we use
2222
const char* anothername; // Optional second name
2323
bool repeatable; // May repeat
24+
unsigned int maxlen; // Maximum length (if nonzero)
2425
};
2526

2627
static IIMtag iimtag[] = {
27-
{ 3, "IPTC:ObjectTypeReference", NULL, false },
28-
{ 4, "IPTC:ObjectAttributeReference", NULL, true },
29-
{ 5, "IPTC:ObjectName", NULL, false },
30-
{ 7, "IPTC:EditStatus", NULL, false },
31-
{ 10, "IPTC:Urgency", NULL, false }, // deprecated by IPTC
32-
{ 12, "IPTC:SubjectReference", NULL, true },
33-
{ 15, "IPTC:Category", NULL, false },
34-
{ 20, "IPTC:SupplementalCategories", NULL, true }, // deprecated by IPTC
35-
{ 22, "IPTC:FixtureIdentifier", NULL, false },
36-
{ 25, "Keywords", NULL, true },
37-
{ 26, "IPTC:ContentLocationCode", NULL, true },
38-
{ 27, "IPTC:ContentLocationName", NULL, true },
39-
{ 30, "IPTC:ReleaseDate", NULL, false },
40-
{ 35, "IPTC:ReleaseTime", NULL, false },
41-
{ 37, "IPTC:ExpirationDate", NULL, false },
42-
{ 38, "IPTC:ExpirationTime", NULL, false },
43-
{ 40, "IPTC:Instructions", NULL, false },
44-
{ 45, "IPTC:ReferenceService", NULL, true },
45-
{ 47, "IPTC:ReferenceDate", NULL, false },
46-
{ 50, "IPTC:ReferenceNumber", NULL, true },
47-
{ 55, "IPTC:DateCreated", NULL, false },
48-
{ 60, "IPTC:TimeCreated", NULL, false },
49-
{ 62, "IPTC:DigitalCreationDate", NULL, false },
50-
{ 63, "IPTC:DigitalCreationTime", NULL, false },
51-
{ 65, "IPTC:OriginatingProgram", "Software", false },
52-
{ 70, "IPTC:ProgramVersion", NULL, false },
53-
{ 80, "IPTC:Creator", "Artist", true }, // sometimes called "byline"
54-
{ 85, "IPTC:AuthorsPosition", NULL, true }, // sometimes "byline title"
55-
{ 90, "IPTC:City", NULL, false },
56-
{ 92, "IPTC:Sublocation", NULL, false },
57-
{ 95, "IPTC:State", NULL, false }, // sometimes "Province/State"
58-
{ 100, "IPTC:CountryCode", NULL, false },
59-
{ 101, "IPTC:Country", NULL, false },
60-
{ 103, "IPTC:TransmissionReference", NULL, false },
61-
{ 105, "IPTC:Headline", NULL, false },
62-
{ 110, "IPTC:Provider", NULL, false }, // aka Credit
63-
{ 115, "IPTC:Source", NULL, false },
64-
{ 116, "IPTC:CopyrightNotice", "Copyright", false },
65-
{ 118, "IPTC:Contact", NULL, false },
66-
{ 120, "IPTC:Caption", "ImageDescription", false },
67-
{ 121, "IPTC:LocalCaption", NULL, false },
68-
{ 122, "IPTC:CaptionWriter", NULL, false }, // aka Writer/Editor
28+
{ 3, "IPTC:ObjectTypeReference", NULL, false, 67 },
29+
{ 4, "IPTC:ObjectAttributeReference", NULL, true, 68 },
30+
{ 5, "IPTC:ObjectName", NULL, false, 64 },
31+
{ 7, "IPTC:EditStatus", NULL, false, 64 },
32+
{ 10, "IPTC:Urgency", NULL, false, 1 }, // deprecated by IPTC
33+
{ 12, "IPTC:SubjectReference", NULL, true, 236 },
34+
{ 15, "IPTC:Category", NULL, false, 3 },
35+
{ 20, "IPTC:SupplementalCategories", NULL, true, 32 }, // deprecated by IPTC
36+
{ 22, "IPTC:FixtureIdentifier", NULL, false, 32 },
37+
{ 25, "IPTC:Keywords", NULL, true, 64 },
38+
{ 26, "IPTC:ContentLocationCode", NULL, true, 3 },
39+
{ 27, "IPTC:ContentLocationName", NULL, true, 64 },
40+
{ 30, "IPTC:ReleaseDate", NULL, false, 8 },
41+
{ 35, "IPTC:ReleaseTime", NULL, false, 11 },
42+
{ 37, "IPTC:ExpirationDate", NULL, false, 8 },
43+
{ 38, "IPTC:ExpirationTime", NULL, false, 11 },
44+
{ 40, "IPTC:Instructions", NULL, false, 256 },
45+
{ 45, "IPTC:ReferenceService", NULL, true, 10 },
46+
{ 47, "IPTC:ReferenceDate", NULL, false, 8 },
47+
{ 50, "IPTC:ReferenceNumber", NULL, true, 8 },
48+
{ 55, "IPTC:DateCreated", NULL, false, 8 },
49+
{ 60, "IPTC:TimeCreated", NULL, false, 11 },
50+
{ 62, "IPTC:DigitalCreationDate", NULL, false, 8 },
51+
{ 63, "IPTC:DigitalCreationTime", NULL, false, 11 },
52+
{ 65, "IPTC:OriginatingProgram", "Software", false, 32 },
53+
{ 70, "IPTC:ProgramVersion", NULL, false, 10 },
54+
{ 80, "IPTC:Creator", "Artist", true, 32 }, // sometimes called "byline"
55+
{ 85, "IPTC:AuthorsPosition", NULL, true, 32 }, // sometimes "byline title"
56+
{ 90, "IPTC:City", NULL, false, 32 },
57+
{ 92, "IPTC:Sublocation", NULL, false, 32 },
58+
{ 95, "IPTC:State", NULL, false, 32 }, // sometimes "Province/State"
59+
{ 100, "IPTC:CountryCode", NULL, false, 3 },
60+
{ 101, "IPTC:Country", NULL, false, 64 },
61+
{ 103, "IPTC:TransmissionReference", NULL, false, 32 },
62+
{ 105, "IPTC:Headline", NULL, false, 256 },
63+
{ 110, "IPTC:Provider", NULL, false, 32 }, // aka Credit
64+
{ 115, "IPTC:Source", NULL, false, 32 },
65+
{ 116, "IPTC:CopyrightNotice", "Copyright", false, 128 },
66+
{ 118, "IPTC:Contact", NULL, false, 128 },
67+
{ 120, "IPTC:Caption", "ImageDescription", false, 2000 },
68+
{ 121, "IPTC:LocalCaption", NULL, false, 256 },
69+
{ 122, "IPTC:CaptionWriter", NULL, false, 32 }, // aka Writer/Editor
6970
// Note: 150-154 is audio sampling stuff
70-
{ 184, "IPTC:JobID", NULL, false },
71-
{ 185, "IPTC:MasterDocumentID", NULL, false },
72-
{ 186, "IPTC:ShortDocumentID", NULL, false },
73-
{ 187, "IPTC:UniqueDocumentID", NULL, false },
74-
{ 188, "IPTC:OwnerID", NULL, false },
75-
{ 221, "IPTC:Prefs", NULL, false },
76-
{ 225, "IPTC:ClassifyState", NULL, false },
77-
{ 228, "IPTC:SimilarityIndex", NULL, false },
78-
{ 230, "IPTC:DocumentNotes", NULL, false },
79-
{ 231, "IPTC:DocumentHistory", NULL, false },
80-
{ -1, NULL, NULL, false }
71+
{ 184, "IPTC:JobID", NULL, false, 64 },
72+
{ 185, "IPTC:MasterDocumentID", NULL, false, 256 },
73+
{ 186, "IPTC:ShortDocumentID", NULL, false, 64 },
74+
{ 187, "IPTC:UniqueDocumentID", NULL, false, 128 },
75+
{ 188, "IPTC:OwnerID", NULL, false, 128 },
76+
{ 221, "IPTC:Prefs", NULL, false, 64 },
77+
{ 225, "IPTC:ClassifyState", NULL, false, 64 },
78+
{ 228, "IPTC:SimilarityIndex", NULL, false, 32 },
79+
{ 230, "IPTC:DocumentNotes", NULL, false, 1024 },
80+
{ 231, "IPTC:DocumentHistory", NULL, false, 256 },
81+
{ -1, NULL, NULL, false, 0 }
8182
};
8283

8384
// N.B. All "Date" fields are 8 digit strings: CCYYMMDD
8485
// All "Time" fields are 11 digit strings (what format?)
8586

87+
// IPTC references:
88+
//
89+
// * https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata
90+
// * https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#iptc-core-schema-1-5-specifications
91+
// This is the one where you can find the length limits
92+
// * ExifTool's documentation about IPTC tags (caveat: not a definitive
93+
// reference, could be outdated or incorrect):
94+
// https://exiftool.org/TagNames/IPTC.html
95+
8696
} // anonymous namespace
8797

8898

@@ -144,9 +154,13 @@ decode_iptc_iim(const void* iptc, int length, ImageSpec& spec)
144154
} else {
145155
spec.attribute(iimtag[i].name, s);
146156
}
157+
#if 0
158+
// We are no longer confident about auto-translating IPTC
159+
// data into allegedly equivalent metadata.
147160
if (iimtag[i].anothername
148161
&& !spec.extra_attribs.contains(iimtag[i].anothername))
149162
spec.attribute(iimtag[i].anothername, s);
163+
#endif
150164
break;
151165
}
152166
}
@@ -193,18 +207,28 @@ encode_iptc_iim(const ImageSpec& spec, std::vector<char>& iptc)
193207
Strutil::split(allvals, tokens, ";");
194208
for (auto& token : tokens) {
195209
token = Strutil::strip(token);
196-
if (token.size())
210+
if (token.size()) {
211+
if (iimtag[i].maxlen && iimtag[i].maxlen < token.size())
212+
token = token.substr(0, iimtag[i].maxlen);
197213
encode_iptc_iim_one_tag(iimtag[i].tag, token, iptc);
214+
}
198215
}
199216
} else {
200217
// Regular, non-repeating
201-
encode_iptc_iim_one_tag(iimtag[i].tag, p->get_string(0), iptc);
218+
std::string token = p->get_string(0);
219+
if (iimtag[i].maxlen && iimtag[i].maxlen < token.size())
220+
token = token.substr(0, iimtag[i].maxlen);
221+
encode_iptc_iim_one_tag(iimtag[i].tag, token, iptc);
202222
}
203223
}
224+
#if 0
225+
// We are no longer confident about auto-translating other metadata
226+
// into allegedly equivalent IPTC.
204227
if (iimtag[i].anothername) {
205228
if ((p = spec.find_attribute(iimtag[i].anothername)))
206229
encode_iptc_iim_one_tag(iimtag[i].tag, p->get_string(0), iptc);
207230
}
231+
#endif
208232
}
209233
return iptc.size() != 0;
210234
}

testsuite/jpeg-metadata/ref/out.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ with-attribs-and-desc.jpg : 640 x 480, 3 channel, uint8 jpeg
8383
Exif:ColorSpace: 1
8484
Exif:ExifVersion: "0230"
8585
Exif:FlashPixVersion: "0100"
86-
IPTC:Caption: "A photo"
8786
jpeg:subsampling: "4:2:0"
8887
oiio:ColorSpace: "sRGB"
8988
Reading src/blender-render.jpg
@@ -109,6 +108,5 @@ with-colon-desc.jpg : 640 x 480, 3 channel, uint8 jpeg
109108
Exif:ColorSpace: 1
110109
Exif:ExifVersion: "0230"
111110
Exif:FlashPixVersion: "0100"
112-
IPTC:Caption: "Example:Text"
113111
jpeg:subsampling: "4:2:0"
114112
oiio:ColorSpace: "sRGB"

0 commit comments

Comments
 (0)