📄 iptc_support.cpp
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2006-2007 Adobe Systems Incorporated// All Rights Reserved//// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms// of the Adobe license agreement accompanying it.// =================================================================================================#include "IPTC_Support.hpp"#include "EndianUtils.hpp"#include "Reconcile_Impl.hpp"// =================================================================================================/// \file IPTC_Support.cpp/// \brief XMPFiles support for IPTC (IIM) DataSets.///// =================================================================================================const DataSetCharacteristics kKnownDataSets[] = { { kIPTC_ObjectType, kIPTC_UnmappedText, 67, "", "" }, // Not mapped to XMP. { kIPTC_IntellectualGenre, kIPTC_MapSpecial, 68, kXMP_NS_IPTCCore, "IntellectualGenre" }, // Only the name part is in the XMP. { kIPTC_Title, kIPTC_MapLangAlt, 64, kXMP_NS_DC, "title" }, { kIPTC_EditStatus, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP. { kIPTC_EditorialUpdate, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. { kIPTC_Urgency, kIPTC_MapSimple, 1, kXMP_NS_Photoshop, "Urgency" }, { kIPTC_SubjectCode, kIPTC_MapSpecial, 236, kXMP_NS_IPTCCore, "SubjectCode" }, // Only the reference number is in the XMP. { kIPTC_Category, kIPTC_MapSimple, 3, kXMP_NS_Photoshop, "Category" }, { kIPTC_SuppCategory, kIPTC_MapArray, 32, kXMP_NS_Photoshop, "SupplementalCategories" }, { kIPTC_FixtureIdentifier, kIPTC_UnmappedText, 32, "", "" }, // Not mapped to XMP. { kIPTC_Keyword, kIPTC_MapArray, 64, kXMP_NS_DC, "subject" }, { kIPTC_ContentLocCode, kIPTC_UnmappedText, 3, "", "" }, // Not mapped to XMP. { kIPTC_ContentLocName, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP. { kIPTC_ReleaseDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. { kIPTC_ReleaseTime, kIPTC_UnmappedText, 11, "", "" }, // Not mapped to XMP. { kIPTC_ExpDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. { kIPTC_ExpTime, kIPTC_UnmappedText, 11, "", "" }, // Not mapped to XMP. { kIPTC_Instructions, kIPTC_MapSimple, 256, kXMP_NS_Photoshop, "Instructions" }, { kIPTC_ActionAdvised, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. { kIPTC_RefService, kIPTC_UnmappedText, 10, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50! { kIPTC_RefDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50! { kIPTC_RefNumber, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50! { kIPTC_DateCreated, kIPTC_MapSpecial, 8, kXMP_NS_Photoshop, "DateCreated" }, // Combined with 2:60, TimeCreated. { kIPTC_TimeCreated, kIPTC_MapSpecial, 11, "", "" }, // Combined with 2:55, DateCreated. { kIPTC_DigitalCreateDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. { kIPTC_DigitalCreateTime, kIPTC_UnmappedText, 11, "", "" }, // Not mapped to XMP. { kIPTC_OriginProgram, kIPTC_UnmappedText, 32, "", "" }, // Not mapped to XMP. { kIPTC_ProgramVersion, kIPTC_UnmappedText, 10, "", "" }, // Not mapped to XMP. { kIPTC_ObjectCycle, kIPTC_UnmappedText, 1, "", "" }, // Not mapped to XMP. { kIPTC_Creator, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Author" }, // ! Aliased to dc:creator[1]. { kIPTC_CreatorJobtitle, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "AuthorsPosition" }, { kIPTC_City, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "City" }, { kIPTC_Location, kIPTC_MapSimple, 32, kXMP_NS_IPTCCore, "Location" }, { kIPTC_State, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "State" }, { kIPTC_CountryCode, kIPTC_MapSimple, 3, kXMP_NS_IPTCCore, "CountryCode" }, { kIPTC_Country, kIPTC_MapSimple, 64, kXMP_NS_Photoshop, "Country" }, { kIPTC_JobID, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "TransmissionReference" }, { kIPTC_Headline, kIPTC_MapSimple, 256, kXMP_NS_Photoshop, "Headline" }, { kIPTC_Provider, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Credit" }, { kIPTC_Source, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Source" }, { kIPTC_CopyrightNotice, kIPTC_MapLangAlt, 128, kXMP_NS_DC, "rights" }, { kIPTC_Contact, kIPTC_UnmappedText, 128, "", "" }, // Not mapped to XMP. { kIPTC_Description, kIPTC_MapLangAlt, 2000, kXMP_NS_DC, "description" }, { kIPTC_DescriptionWriter, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "CaptionWriter" }, { kIPTC_RasterizedCaption, kIPTC_UnmappedBin, 7360, "", "" }, // Not mapped to XMP. ! Binary data! { kIPTC_ImageType, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. { kIPTC_ImageOrientation, kIPTC_UnmappedText, 1, "", "" }, // Not mapped to XMP. { kIPTC_LanguageID, kIPTC_UnmappedText, 3, "", "" }, // Not mapped to XMP. { kIPTC_AudioType, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. { kIPTC_AudioSampleRate, kIPTC_UnmappedText, 6, "", "" }, // Not mapped to XMP. { kIPTC_AudioSampleRes, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. { kIPTC_AudioDuration, kIPTC_UnmappedText, 6, "", "" }, // Not mapped to XMP. { kIPTC_AudioOutcue, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP. { kIPTC_PreviewFormat, kIPTC_UnmappedBin, 2, "", "" }, // Not mapped to XMP. ! Binary data! { kIPTC_PreviewFormatVers, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. ! Binary data! { kIPTC_PreviewData, kIPTC_UnmappedText, 256000, "", "" }, // Not mapped to XMP. ! Binary data! { 255, kIPTC_MapSpecial, 0, 0, 0 } }; // ! Must be last as a sentinel.// A combination of the IPTC "Subject Reference System Guidelines" and IIMv4.1 Appendix G.const IntellectualGenreMapping kIntellectualGenreMappings[] ={ { "001", "Current" }, { "002", "Analysis" }, { "003", "Archive material" }, { "004", "Background" }, { "005", "Feature" }, { "006", "Forecast" }, { "007", "History" }, { "008", "Obituary" }, { "009", "Opinion" }, { "010", "Polls and surveys" }, { "010", "Polls & Surveys" }, { "011", "Profile" }, { "012", "Results listings and statistics" }, { "012", "Results Listings & Tables" }, { "013", "Side bar and supporting information" }, { "013", "Side bar & Supporting information" }, { "014", "Summary" }, { "015", "Transcript and verbatim" }, { "015", "Transcript & Verbatim" }, { "016", "Interview" }, { "017", "From the scene" }, { "017", "From the Scene" }, { "018", "Retrospective" }, { "019", "Synopsis" }, { "019", "Statistics" }, { "020", "Update" }, { "021", "Wrapup" }, { "021", "Wrap-up" }, { "022", "Press release" }, { "022", "Press Release" }, { "023", "Quote" }, { "024", "Press-digest" }, { "025", "Review" }, { "026", "Curtain raiser" }, { "027", "Actuality" }, { "028", "Question and answer" }, { "029", "Music" }, { "030", "Response to a question" }, { "031", "Raw sound" }, { "032", "Scener" }, { "033", "Text only" }, { "034", "Voicer" }, { "035", "Fixture" }, { 0, 0 } }; // ! Must be last as a sentinel. // =================================================================================================// FindKnownDataSet// ================static const DataSetCharacteristics* FindKnownDataSet ( XMP_Uns8 id ){ size_t i = 0; while ( kKnownDataSets[i].id < id ) ++i; // The list is short enough for a linear search. if ( kKnownDataSets[i].id != id ) return 0; return &kKnownDataSets[i]; } // FindKnownDataSet// =================================================================================================// IPTC_Manager::ParseMemoryDataSets// =================================//// Parse the legacy IIM block, keeping information about all 2:* DataSets and size of other records.void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ){ // Get rid of any existing data. DataSetMap::iterator dsPos = this->dataSets.begin(); DataSetMap::iterator dsEnd = this->dataSets.end(); for ( ; dsPos != dsEnd; ++dsPos ) this->DisposeLooseValue ( dsPos->second ); this->dataSets.clear(); if ( this->ownedContent ) free ( this->iptcContent ); this->ownedContent = false; // Set to true later if the content is copied. this->iptcContent = 0; this->iptcLength = 0; this->changed = false; if ( length == 0 ) return; if ( *((XMP_Uns8*)data) != 0x1C ) XMP_Throw ( "Not valid IPTC, no leading 0x1C", kXMPErr_BadIPTC ); // Allocate space for the full in-memory data and copy it. if ( length > 10*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based IPTC", kXMPErr_BadIPTC ); this->iptcLength = length; if ( ! copyData ) { this->iptcContent = (XMP_Uns8*)data; } else { this->iptcContent = (XMP_Uns8*) malloc(length); if ( this->iptcContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( this->iptcContent, data, length ); // AUDIT: Safe, malloc'ed length bytes above. this->ownedContent = true; } // Build the map of the 2:* DataSets. XMP_Uns8* iptcPtr = this->iptcContent; XMP_Uns8* iptcEnd = iptcPtr + length; XMP_Uns8* iptcLimit = iptcEnd - kMinDataSetSize; XMP_Uns32 dsLen; // ! The large form can have values up to 4GB in length. this->utf8Encoding = false; bool foundRec2 = false; for ( ; iptcPtr <= iptcLimit; iptcPtr += dsLen ) { XMP_Uns8* dsPtr = iptcPtr; XMP_Uns8 oneC = *iptcPtr; XMP_Uns8 recNum = *(iptcPtr+1); XMP_Uns8 dsNum = *(iptcPtr+2); if ( oneC != 0x1C ) break; // No more DataSets. dsLen = GetUns16BE ( iptcPtr+3 ); // ! Compute dsLen before any "continue", needed for loop increment! iptcPtr += 5; // Advance to the data (or extended length). if ( (dsLen & 0x8000) != 0 ) { XMP_Assert ( dsLen <= 0xFFFF ); XMP_Uns32 lenLen = dsLen & 0x7FFF; if ( iptcPtr > iptcEnd-lenLen ) break; // Bad final DataSet. Throw instead? dsLen = 0; for ( XMP_Uns16 i = 0; i < lenLen; ++i, ++iptcPtr ) { dsLen = (dsLen << 8) + *iptcPtr; } } if ( iptcPtr > (iptcEnd - dsLen) ) break; // Bad final DataSet. Throw instead? if ( recNum == 0 ) continue; // Should not be a record 0. Throw instead? if ( recNum == 1 ) { if ( (dsNum == 90) && (dsLen >= 3) ) { if ( memcmp ( iptcPtr, "\x1B\x25\x47", 3 ) == 0 ) this->utf8Encoding = true; } continue; // Ignore all other record 1 DataSets. } if ( recNum == 2 ) { if ( ! foundRec2 ) { foundRec2 = true; this->rec2Offset = dsPtr - this->iptcContent; this->rec2Length = this->iptcLength - this->rec2Offset; // ! In case there are no other records. } } else { this->rec2Length = dsPtr - (this->iptcContent + this->rec2Offset); break; // The records are in ascending order, done. } XMP_Assert ( recNum == 2 ); if ( dsNum == 0 ) continue; // Ignore 2:00 when reading. DataSetInfo dsInfo ( dsNum, dsLen, iptcPtr ); DataSetMap::iterator dsPos = this->dataSets.find ( dsNum ); bool repeatable = false; const DataSetCharacteristics* knownDS = FindKnownDataSet ( dsNum ); if ( (knownDS == 0) || (knownDS->mapForm == kIPTC_MapArray) ) { repeatable = true; // Allow repeats for unknown DataSets. } else if ( dsNum == kIPTC_SubjectCode ) { repeatable = true; } if ( repeatable || (dsPos == this->dataSets.end()) ) { DataSetMap::value_type mapValue ( dsNum, dsInfo ); (void) this->dataSets.insert ( this->dataSets.upper_bound ( dsNum ), mapValue ); } else { this->DisposeLooseValue ( dsPos->second ); dsPos->second = dsInfo; // Keep the last copy of illegal repeats. } }} // IPTC_Manager::ParseMemoryDataSets// =================================================================================================// IPTC_Manager::GetDataSet// ========================size_t IPTC_Manager::GetDataSet ( XMP_Uns8 id, DataSetInfo* info, size_t which /* = 0 */ ) const{ DataSetMap::const_iterator dsPos = this->dataSets.lower_bound ( id ); if ( (dsPos == this->dataSets.end()) || (id != dsPos->second.id) ) return 0; size_t dsCount = this->dataSets.count ( id ); if ( which >= dsCount ) return 0; // Valid range for which is 0 .. count-1. if ( info != 0 ) { for ( size_t i = 0; i < which; ++i ) ++dsPos; // ??? dsPos += which; *info = dsPos->second; } return dsCount;} // IPTC_Manager::GetDataSet// =================================================================================================// IPTC_Manager::GetDataSet_UTF8// ============================= size_t IPTC_Manager::GetDataSet_UTF8 ( XMP_Uns8 id, std::string * utf8Str, size_t which /* = 0 */ ) const{ DataSetInfo dsInfo; size_t dsCount = GetDataSet ( id, &dsInfo, which ); if ( dsCount == 0 ) return 0; if ( utf8Str != 0 ) { if ( this->utf8Encoding ) { utf8Str->assign ( (char*)dsInfo.dataPtr, dsInfo.dataLen ); } else { ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, utf8Str ); } } return dsCount; } // IPTC_Manager::GetDataSet_UTF8// =================================================================================================// IPTC_Manager::DisposeLooseValue// ===============================//// Dispose of loose values from SetDataSet calls after the last UpdateMemoryDataSets.// ! Don't try to make the DataSetInfo struct be self-cleaning. It is a primary public type, returned// ! from GetDataSet. Making it self-cleaning would get into nasty assignment and pointer ownership// ! issues, far worse than doing this explicit cleanup.void IPTC_Manager::DisposeLooseValue ( DataSetInfo & dsInfo ){ if ( dsInfo.dataLen == 0 ) return; XMP_Uns8* dataBegin = this->iptcContent; XMP_Uns8* dataEnd = dataBegin + this->iptcLength; if ( ((XMP_Uns8*)dsInfo.dataPtr < dataBegin) || ((XMP_Uns8*)dsInfo.dataPtr >= dataEnd) ) { free ( (void*) dsInfo.dataPtr ); dsInfo.dataPtr = 0; } } // IPTC_Manager::DisposeLooseValue// =================================================================================================// =================================================================================================// =================================================================================================// IPTC_Writer::~IPTC_Writer// =========================//// Dispose of loose values from SetDataSet calls after the last UpdateMemoryDataSets.IPTC_Writer::~IPTC_Writer(){ DataSetMap::iterator dsPos = this->dataSets.begin(); DataSetMap::iterator dsEnd = this->dataSets.end(); for ( ; dsPos != dsEnd; ++dsPos ) this->DisposeLooseValue ( dsPos->second ); } // IPTC_Writer::~IPTC_Writer// =================================================================================================// IPTC_Writer::SetDataSet_UTF8
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -