1
// ***************************************************************** -*- C++ -*-
3
* Copyright (C) 2004, 2005 Andreas Huggel <ahuggel@gmx.net>
5
* This program is part of the Exiv2 distribution.
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* as published by the Free Software Foundation; either version 2
10
* of the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
Author(s): Brad Schick (brad) <brad@robotbattle.com>
25
History: 24-Jul-04, brad: created
27
// *****************************************************************************
29
EXIV2_RCSID("@(#) $Id: datasets.cpp 560 2005-04-17 11:51:32Z ahuggel $");
31
// *****************************************************************************
32
// included header files
33
#include "datasets.hpp"
37
#include "metadatum.hpp"
43
// *****************************************************************************
44
// class member definitions
59
: number_(number), name_(name), desc_(desc), mandatory_(mandatory),
60
repeatable_(repeatable), minbytes_(minbytes), maxbytes_(maxbytes),
61
type_(type), recordId_(recordId), photoshop_(photoshop)
65
RecordInfo::RecordInfo(
70
: recordId_(recordId), name_(name), desc_(desc)
74
const RecordInfo IptcDataSets::recordInfo_[] = {
75
RecordInfo(IptcDataSets::invalidRecord, "(invalid)", "(invalid)"),
76
RecordInfo(IptcDataSets::envelope, "Envelope", "IIM envelope record"),
77
RecordInfo(IptcDataSets::application2, "Application2", "IIM application record 2"),
80
static const DataSet envelopeRecord[] = {
81
DataSet(IptcDataSets::ModelVersion, "ModelVersion", "Version of IIM part 1", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
82
DataSet(IptcDataSets::Destination, "Destination", "Routing information", false, true, 0, 1024, Exiv2::string, IptcDataSets::envelope, ""),
83
DataSet(IptcDataSets::FileFormat, "FileFormat", "IIM appendix A file format", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
84
DataSet(IptcDataSets::FileVersion, "FileVersion", "File format version", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
85
DataSet(IptcDataSets::ServiceId, "ServiceId", "Identifies the provider and product", true, false, 0, 10, Exiv2::string, IptcDataSets::envelope, ""),
86
DataSet(IptcDataSets::EnvelopeNumber, "EnvelopeNumber", "Combined unique identification", true, false, 8, 8, Exiv2::string, IptcDataSets::envelope, ""),
87
DataSet(IptcDataSets::ProductId, "ProductId", "Identifies service subset", false, true, 0, 32, Exiv2::string, IptcDataSets::envelope, ""),
88
DataSet(IptcDataSets::EnvelopePriority, "EnvelopePriority", "Envelope handling priority", false, false, 1, 1, Exiv2::string, IptcDataSets::envelope, ""),
89
DataSet(IptcDataSets::DateSent, "DateSent", "Date material was sent", true, false, 8, 8, Exiv2::date, IptcDataSets::envelope, ""),
90
DataSet(IptcDataSets::TimeSent, "TimeSent", "Time material was sent", false, false, 11, 11, Exiv2::time, IptcDataSets::envelope, ""),
91
DataSet(IptcDataSets::CharacterSet, "CharacterSet", "Specifies character sets", false, false, 0, 32, Exiv2::undefined, IptcDataSets::envelope, ""),
92
DataSet(IptcDataSets::UNO, "UNO", "Unique Name of Object", false, false, 14, 80, Exiv2::string, IptcDataSets::envelope, ""),
93
DataSet(IptcDataSets::ARMId, "ARMId", "Abstract Relationship Method identifier", false, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
94
DataSet(IptcDataSets::ARMVersion, "ARMVersion", "Abstract Relationship Method version", false, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
95
DataSet(0xffff, "(Invalid)", "(Invalid)", false, false, 0, 0, Exiv2::unsignedShort, IptcDataSets::envelope, "")
98
static const DataSet application2Record[] = {
99
DataSet(IptcDataSets::RecordVersion, "RecordVersion", "Version of IIM part 2", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::application2, ""),
100
DataSet(IptcDataSets::ObjectType, "ObjectType", "IIM appendix G object type", false, false, 3, 67, Exiv2::string, IptcDataSets::application2, ""),
101
DataSet(IptcDataSets::ObjectAttribute, "ObjectAttribute", "IIM appendix G object attribute", false, true, 4, 68, Exiv2::string, IptcDataSets::application2, ""),
102
DataSet(IptcDataSets::ObjectName, "ObjectName", "Shorthand reference of content", false, false, 0, 64, Exiv2::string, IptcDataSets::application2, "Document title"),
103
DataSet(IptcDataSets::EditStatus, "EditStatus", "Content status", false, false, 0, 64, Exiv2::string, IptcDataSets::application2, ""),
104
DataSet(IptcDataSets::EditorialUpdate, "EditorialUpdate", "Indicates the type of update", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
105
DataSet(IptcDataSets::Urgency, "Urgency", "Editorial urgency of content", false, false, 1, 1, Exiv2::string, IptcDataSets::application2, "Urgency"),
106
DataSet(IptcDataSets::Subject, "Subject", "Structured definition of the subject", false, true, 13, 236, Exiv2::string, IptcDataSets::application2, ""),
107
DataSet(IptcDataSets::Category, "Category", "Identifies the subject", false, false, 0, 3, Exiv2::string, IptcDataSets::application2, "Category"),
108
DataSet(IptcDataSets::SuppCategory, "SuppCategory", "Refines the subject", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, "Supplemental Categories"),
109
DataSet(IptcDataSets::FixtureId, "FixtureId", "Identifies content that recurs", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, ""),
110
DataSet(IptcDataSets::Keywords, "Keywords", "Information retrieval words", false, true, 0, 64, Exiv2::string, IptcDataSets::application2, "Keywords"),
111
DataSet(IptcDataSets::LocationCode, "LocationCode", "ISO country code for content", false, true, 3, 3, Exiv2::string, IptcDataSets::application2, ""),
112
DataSet(IptcDataSets::LocationName, "LocationName", "Full country name for content", false, true, 0, 64, Exiv2::string, IptcDataSets::application2, ""),
113
DataSet(IptcDataSets::ReleaseDate, "ReleaseDate", "Earliest intended usable date", false, false, 8, 8, Exiv2::date, IptcDataSets::application2, ""),
114
DataSet(IptcDataSets::ReleaseTime, "ReleaseTime", "Earliest intended usable time", false, false, 11, 11, Exiv2::time, IptcDataSets::application2, ""),
115
DataSet(IptcDataSets::ExpirationDate, "ExpirationDate", "Latest intended usable date", false, false, 8, 8, Exiv2::date, IptcDataSets::application2, ""),
116
DataSet(IptcDataSets::ExpirationTime, "ExpirationTime", "Latest intended usable time", false, false, 11, 11, Exiv2::time, IptcDataSets::application2, ""),
117
DataSet(IptcDataSets::SpecialInstructions, "SpecialInstructions", "Editorial usage instructions", false, false, 0, 256, Exiv2::string, IptcDataSets::application2, "Instructions"),
118
DataSet(IptcDataSets::ActionAdvised, "ActionAdvised", "Action provided to previous data", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
119
DataSet(IptcDataSets::ReferenceService, "ReferenceService", "Service Identifier of a prior envelope", false, true, 0, 10, Exiv2::string, IptcDataSets::application2, ""),
120
DataSet(IptcDataSets::ReferenceDate, "ReferenceDate", "Date of a prior envelope", false, true, 8, 8, Exiv2::date, IptcDataSets::application2, ""),
121
DataSet(IptcDataSets::ReferenceNumber, "ReferenceNumber", "Envelope Number of a prior envelope", false, true, 8, 8, Exiv2::string, IptcDataSets::application2, ""),
122
DataSet(IptcDataSets::DateCreated, "DateCreated", "Creation date of intellectual content", false, false, 8, 8, Exiv2::date, IptcDataSets::application2, "Date created"),
123
DataSet(IptcDataSets::TimeCreated, "TimeCreated", "Creation time of intellectual content", false, false, 11, 11, Exiv2::time, IptcDataSets::application2, ""),
124
DataSet(IptcDataSets::DigitizationDate, "DigitizationDate", "Creation date of digital representation", false, false, 8, 8, Exiv2::date, IptcDataSets::application2, ""),
125
DataSet(IptcDataSets::DigitizationTime, "DigitizationTime", "Creation time of digital representation", false, false, 11, 11, Exiv2::time, IptcDataSets::application2, ""),
126
DataSet(IptcDataSets::Program, "Program", "Content creation program", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, ""),
127
DataSet(IptcDataSets::ProgramVersion, "ProgramVersion", "Content creation program version", false, false, 0, 10, Exiv2::string, IptcDataSets::application2, ""),
128
DataSet(IptcDataSets::ObjectCycle, "ObjectCycle", "Morning, evening, or both", false, false, 1, 1, Exiv2::string, IptcDataSets::application2, ""),
129
DataSet(IptcDataSets::Byline, "Byline", "Name of content creator", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, "Author"),
130
DataSet(IptcDataSets::BylineTitle, "BylineTitle", "Title of content creator", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, "Authors Position"),
131
DataSet(IptcDataSets::City, "City", "City of content origin", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, "City"),
132
DataSet(IptcDataSets::SubLocation, "SubLocation", "Location within city", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, ""),
133
DataSet(IptcDataSets::ProvinceState, "ProvinceState", "Province/State of content origin", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, "State/Province"),
134
DataSet(IptcDataSets::CountryCode, "CountryCode", "ISO country code of content origin", false, false, 3, 3, Exiv2::string, IptcDataSets::application2, ""),
135
DataSet(IptcDataSets::CountryName, "CountryName", "Full country name of content origin", false, false, 0, 64, Exiv2::string, IptcDataSets::application2, "Country"),
136
DataSet(IptcDataSets::TransmissionReference, "TransmissionReference", "Location of original transmission", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, "Transmission Reference"),
137
DataSet(IptcDataSets::Headline, "Headline", "Content synopsis", false, false, 0, 256, Exiv2::string, IptcDataSets::application2, "Headline"),
138
DataSet(IptcDataSets::Credit, "Credit", "Content provider", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, "Credit"),
139
DataSet(IptcDataSets::Source, "Source", "Original owner of content", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, "Source"),
140
DataSet(IptcDataSets::Copyright, "Copyright", "Necessary copyright notice", false, false, 0, 128, Exiv2::string, IptcDataSets::application2, "Copyright notice"),
141
DataSet(IptcDataSets::Contact, "Contact", "Person or organisation to contact", false, true, 0, 128, Exiv2::string, IptcDataSets::application2, ""),
142
DataSet(IptcDataSets::Caption, "Caption", "Content description", false, false, 0, 2000, Exiv2::string, IptcDataSets::application2, "Description"),
143
DataSet(IptcDataSets::Writer, "Writer", "Person responsible for caption", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, "Description writer"),
144
DataSet(IptcDataSets::RasterizedCaption, "RasterizedCaption", "Black and white caption image", false, false, 7360, 7360, Exiv2::undefined, IptcDataSets::application2, ""),
145
DataSet(IptcDataSets::ImageType, "ImageType", "Color components in an image", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
146
DataSet(IptcDataSets::ImageOrientation, "ImageOrientation", "Indicates the layout of an image", false, false, 1, 1, Exiv2::string, IptcDataSets::application2, ""),
147
DataSet(IptcDataSets::Language, "Language", "ISO 639:1988 language code", false, false, 2, 3, Exiv2::string, IptcDataSets::application2, ""),
148
DataSet(IptcDataSets::AudioType, "AudioType", "Information about audio content", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
149
DataSet(IptcDataSets::AudioRate, "AudioRate", "Sampling rate of audio content", false, false, 6, 6, Exiv2::string, IptcDataSets::application2, ""),
150
DataSet(IptcDataSets::AudioResolution, "AudioResolution", "Sampling resolution of audio content", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
151
DataSet(IptcDataSets::AudioDuration, "AudioDuration", "Duration of audio content", false, false, 6, 6, Exiv2::string, IptcDataSets::application2, ""),
152
DataSet(IptcDataSets::AudioOutcue, "AudioOutcue", "Final words or sounds of audio content", false, false, 0, 64, Exiv2::string, IptcDataSets::application2, ""),
153
DataSet(IptcDataSets::PreviewFormat, "PreviewFormat", "IIM appendix A file format of preview", false, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::application2, ""),
154
DataSet(IptcDataSets::PreviewVersion, "PreviewVersion", "File format version of preview", false, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::application2, ""),
155
DataSet(IptcDataSets::Preview, "Preview", "Binary preview data", false, false, 0, 256000, Exiv2::undefined, IptcDataSets::application2, ""),
156
DataSet(0xffff, "(Invalid)", "(Invalid)", false, false, 0, 0, Exiv2::unsignedShort, IptcDataSets::application2, "")
159
static const DataSet unknownDataSet(0xffff, "Unknown dataset", "Unknown dataset", false, true, 0, 0xffffffff, Exiv2::string, IptcDataSets::invalidRecord, "Unknown dataset");
161
// Dataset lookup lists.This is an array with pointers to one list per IIM4 Record.
162
// The record id is used as the index into the array.
163
const DataSet* IptcDataSets::records_[] = {
165
envelopeRecord, application2Record,
169
int IptcDataSets::dataSetIdx(uint16_t number, uint16_t recordId)
171
if( recordId != envelope && recordId != application2 ) return -1;
172
const DataSet* dataSet = records_[recordId];
173
if (dataSet == 0) return -1;
175
for (idx = 0; dataSet[idx].number_ != number; ++idx) {
176
if (dataSet[idx].number_ == 0xffff) return -1;
181
int IptcDataSets::dataSetIdx(const std::string& dataSetName, uint16_t recordId)
183
if( recordId != envelope && recordId != application2 ) return -1;
184
const DataSet* dataSet = records_[recordId];
185
if (dataSet == 0) return -1;
187
for (idx = 0; dataSet[idx].name_ != dataSetName; ++idx) {
188
if (dataSet[idx].number_ == 0xffff) return -1;
193
TypeId IptcDataSets::dataSetType(uint16_t number, uint16_t recordId)
195
int idx = dataSetIdx(number, recordId);
196
if (idx == -1) return unknownDataSet.type_;
197
return records_[recordId][idx].type_;
200
std::string IptcDataSets::dataSetName(uint16_t number, uint16_t recordId)
202
int idx = dataSetIdx(number, recordId);
203
if (idx != -1) return records_[recordId][idx].name_;
205
std::ostringstream os;
206
os << "0x" << std::setw(4) << std::setfill('0') << std::right
207
<< std::hex << number;
211
const char* IptcDataSets::dataSetDesc(uint16_t number, uint16_t recordId)
213
int idx = dataSetIdx(number, recordId);
214
if (idx == -1) return unknownDataSet.desc_;
215
return records_[recordId][idx].desc_;
218
const char* IptcDataSets::dataSetPsName(uint16_t number, uint16_t recordId)
220
int idx = dataSetIdx(number, recordId);
221
if (idx == -1) return unknownDataSet.photoshop_;
222
return records_[recordId][idx].photoshop_;
225
bool IptcDataSets::dataSetRepeatable(uint16_t number, uint16_t recordId)
227
int idx = dataSetIdx(number, recordId);
228
if (idx == -1) return unknownDataSet.repeatable_;
229
return records_[recordId][idx].repeatable_;
232
uint16_t IptcDataSets::dataSet(const std::string& dataSetName,
236
int idx = dataSetIdx(dataSetName, recordId);
238
// dataSetIdx checks the range of recordId
239
dataSet = records_[recordId][idx].number_;
242
if (!isHex(dataSetName, 4, "0x")) throw Error(4, dataSetName);
243
std::istringstream is(dataSetName);
244
is >> std::hex >> dataSet;
249
std::string IptcDataSets::recordName(uint16_t recordId)
251
if (recordId == envelope || recordId == application2) {
252
return recordInfo_[recordId].name_;
255
std::ostringstream os;
256
os << "0x" << std::setw(4) << std::setfill('0') << std::right
257
<< std::hex << recordId;
261
const char* IptcDataSets::recordDesc(uint16_t recordId)
263
if (recordId != envelope && recordId != application2) {
264
return unknownDataSet.desc_;
266
return recordInfo_[recordId].desc_;
269
uint16_t IptcDataSets::recordId(const std::string& recordName)
272
for (i = application2; i > 0; --i) {
273
if (recordInfo_[i].name_ == recordName) break;
276
if (!isHex(recordName, 4, "0x")) throw Error(5, recordName);
277
std::istringstream is(recordName);
283
void IptcDataSets::dataSetList(std::ostream& os)
285
const int count = sizeof(records_)/sizeof(records_[0]);
286
for (int i=0; i < count; ++i) {
287
const DataSet *record = records_[i];
288
for (int j=0; record != 0 && record[j].number_ != 0xffff; ++j) {
289
os << record[j] << "\n";
292
} // IptcDataSets::dataSetList
294
const char* IptcKey::familyName_ = "Iptc";
296
IptcKey::IptcKey(const std::string& key)
302
IptcKey::IptcKey(uint16_t tag, uint16_t record)
303
: tag_(tag), record_(record)
308
IptcKey::IptcKey(const IptcKey& rhs)
309
: tag_(rhs.tag_), record_(rhs.record_), key_(rhs.key_)
313
IptcKey& IptcKey::operator=(const IptcKey& rhs)
315
if (this == &rhs) return *this;
318
record_ = rhs.record_;
323
IptcKey::AutoPtr IptcKey::clone() const
325
return AutoPtr(clone_());
328
IptcKey* IptcKey::clone_() const
330
return new IptcKey(*this);
333
void IptcKey::decomposeKey()
335
// Get the family name, record name and dataSet name parts of the key
336
std::string::size_type pos1 = key_.find('.');
337
if (pos1 == std::string::npos) throw Error(6, key_);
338
std::string familyName = key_.substr(0, pos1);
339
if (familyName != std::string(familyName_)) {
340
throw Error(6, key_);
342
std::string::size_type pos0 = pos1 + 1;
343
pos1 = key_.find('.', pos0);
344
if (pos1 == std::string::npos) throw Error(6, key_);
345
std::string recordName = key_.substr(pos0, pos1 - pos0);
346
if (recordName == "") throw Error(6, key_);
347
std::string dataSetName = key_.substr(pos1 + 1);
348
if (dataSetName == "") throw Error(6, key_);
350
// Use the parts of the key to find dataSet and recordId
351
uint16_t recId = IptcDataSets::recordId(recordName);
352
uint16_t dataSet = IptcDataSets::dataSet(dataSetName, recId);
354
// Possibly translate hex name parts (0xabcd) to real names
355
recordName = IptcDataSets::recordName(recId);
356
dataSetName = IptcDataSets::dataSetName(dataSet, recId);
360
key_ = familyName + "." + recordName + "." + dataSetName;
361
} // IptcKey::decomposeKey
363
void IptcKey::makeKey()
365
key_ = std::string(familyName_)
366
+ "." + IptcDataSets::recordName(record_)
367
+ "." + IptcDataSets::dataSetName(tag_, record_);
370
// *************************************************************************
373
std::ostream& operator<<(std::ostream& os, const DataSet& dataSet)
375
IptcKey iptcKey(dataSet.number_, dataSet.recordId_);
376
return os << dataSet.name_ << ", "
377
<< std::dec << dataSet.number_ << ", "
378
<< "0x" << std::setw(4) << std::setfill('0')
379
<< std::right << std::hex << dataSet.number_ << ", "
380
<< IptcDataSets::recordName(dataSet.recordId_) << ", "
381
<< std::boolalpha << dataSet.mandatory_ << ", "
382
<< dataSet.repeatable_ << ", "
383
<< std::dec << dataSet.minbytes_ << ", "
384
<< dataSet.maxbytes_ << ", "
385
<< iptcKey.key() << ", "
386
<< TypeInfo::typeName(
387
IptcDataSets::dataSetType(dataSet.number_,
388
dataSet.recordId_)) << ", "