📄 jpeg_handler.cpp
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2002-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 "JPEG_Handler.hpp"#include "TIFF_Support.hpp"#include "PSIR_Support.hpp"#include "IPTC_Support.hpp"#include "ReconcileLegacy.hpp"#include "MD5.h"using namespace std;// =================================================================================================/// \file JPEG_Handler.cpp/// \brief File format handler for JPEG.////// This handler ...///// =================================================================================================static const char * kExifSignatureString = "Exif\0\x00";static const char * kExifSignatureAltStr = "Exif\0\xFF";static const size_t kExifSignatureLength = 6;static const size_t kExifMaxDataLength = 0xFFFF - 2 - kExifSignatureLength;static const char * kPSIRSignatureString = "Photoshop 3.0\0";static const size_t kPSIRSignatureLength = 14;static const size_t kPSIRMaxDataLength = 0xFFFF - 2 - kPSIRSignatureLength;static const char * kMainXMPSignatureString = "http://ns.adobe.com/xap/1.0/\0";static const size_t kMainXMPSignatureLength = 29;static const char * kExtXMPSignatureString = "http://ns.adobe.com/xmp/extension/\0";static const size_t kExtXMPSignatureLength = 35;static const size_t kExtXMPPrefixLength = kExtXMPSignatureLength + 32 + 4 + 4;typedef std::map < XMP_Uns32 /* offset */, std::string /* portion */ > ExtXMPPortions;struct ExtXMPContent { XMP_Uns32 length; ExtXMPPortions portions; ExtXMPContent() : length(0) {}; ExtXMPContent ( XMP_Uns32 _length ) : length(_length) {};};typedef std::map < JPEG_MetaHandler::GUID_32 /* guid */, ExtXMPContent /* content */ > ExtendedXMPInfo;#ifndef Trace_UnlimitedJPEG #define Trace_UnlimitedJPEG 0#endif// =================================================================================================// JPEG_MetaHandlerCTor// ====================XMPFileHandler * JPEG_MetaHandlerCTor ( XMPFiles * parent ){ return new JPEG_MetaHandler ( parent );} // JPEG_MetaHandlerCTor// =================================================================================================// JPEG_CheckFormat// ================// For JPEG we just check for the initial SOI standalone marker followed by any of the other markers// that might, well, follow it. A more aggressive check might be to read 4KB then check for legit// marker segments within that portion. Probably won't buy much, and thrashes the dCache more. We// tolerate only a small amount of 0xFF padding between the SOI and following marker. This formally// violates the rules of JPEG, but in practice there won't be any padding anyway.//// ! The CheckXyzFormat routines don't track the filePos, that is left to ScanXyzFile.bool JPEG_CheckFormat ( XMP_FileFormat format, XMP_StringPtr filePath, LFA_FileRef fileRef, XMPFiles * parent ){ IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent); XMP_Assert ( format == kXMP_JPEGFile ); IOBuffer ioBuf; LFA_Seek ( fileRef, 0, SEEK_SET ); if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; // We need at least 4, the buffer is filled anyway. // First look for the SOI standalone marker. Then skip all 0xFF bytes, padding plus the high // order byte of the next marker. Finally see if the next marker is legit. if ( ! CheckBytes ( ioBuf.ptr, "\xFF\xD8", 2 ) ) return false; ioBuf.ptr += 2; // Move past the SOI. while ( (ioBuf.ptr < ioBuf.limit) && (*ioBuf.ptr == 0xFF) ) ++ioBuf.ptr; if ( ioBuf.ptr == ioBuf.limit ) return false; XMP_Uns8 id = *ioBuf.ptr; if ( id >= 0xDD ) return true; // The most probable cases. if ( (id < 0xC0) || ((id & 0xF8) == 0xD0) || (id == 0xD8) || (id == 0xDA) || (id == 0xDC) ) return false; return true; } // JPEG_CheckFormat// =================================================================================================// JPEG_MetaHandler::JPEG_MetaHandler// ==================================JPEG_MetaHandler::JPEG_MetaHandler ( XMPFiles * _parent ) : exifMgr(0), psirMgr(0), iptcMgr(0), skipReconcile(false){ this->parent = _parent; this->handlerFlags = kJPEG_HandlerFlags; this->stdCharForm = kXMP_Char8Bit;} // JPEG_MetaHandler::JPEG_MetaHandler// =================================================================================================// JPEG_MetaHandler::~JPEG_MetaHandler// ===================================JPEG_MetaHandler::~JPEG_MetaHandler(){ if ( exifMgr != 0 ) delete ( exifMgr ); if ( psirMgr != 0 ) delete ( psirMgr ); if ( iptcMgr != 0 ) delete ( iptcMgr ); } // JPEG_MetaHandler::~JPEG_MetaHandler// =================================================================================================// TableOrDataMarker// =================//// Returns true if the marker is for a table or data marker segment:// FFC4 - DHT// FFCC - DAC// FFDB - DQT// FFDC - DNL// FFDD - DRI// FFDE - DHP// FFDF - EXP// FFEn - APPn// FFFE - COMstatic inline bool TableOrDataMarker ( XMP_Uns16 marker ){ if ( (marker & 0xFFF0) == 0xFFE0 ) return true; // APPn is probably the most common case. if ( marker < 0xFFC4 ) return false; if ( marker == 0xFFC4 ) return true; if ( marker < 0xFFCC ) return false; if ( marker == 0xFFCC ) return true; if ( marker < 0xFFDB ) return false; if ( marker <= 0xFFDF ) return true; if ( marker < 0xFFFE ) return false; if ( marker == 0xFFFE ) return true; return false;} // TableOrDataMarker// =================================================================================================// JPEG_MetaHandler::CacheFileData// ===============================//// Look for the Exif metadata, Photoshop image resources, and XMP in a JPEG (JFIF) file. The native// thumbnail is inside the Exif. The general layout of a JPEG file is:// SOI marker, 2 bytes, 0xFFD8// Marker segments for tables and metadata// SOFn marker segment// Image data// EOI marker, 2 bytes, 0xFFD9//// Each marker segment begins with a 2 byte big endian marker and a 2 byte big endian length. The// length includes the 2 bytes of the length field but not the marker. The high order byte of a // marker is 0xFF, the low order byte tells what kind of marker. A marker can be preceeded by any// number of 0xFF fill bytes, however there are no alignment constraints.//// There are virtually no constraints on the order of the marker segments before the SOFn. A reader// must be prepared to handle any order.//// The Exif metadata is in an APP1 marker segment with a 6 byte signature string of "Exif\0\0" at// the start of the data. The rest of the data is a TIFF stream.//// The Photoshop image resources are in an APP13 marker segment with a 14 byte signature string of// "Photoshop 3.0\0". The rest of the data is a sequence of image resources.//// The main XMP is in an APP1 marker segment with a 29 byte signature string of// "http://ns.adobe.com/xap/1.0/\0". The rest of the data is the serialized XMP packet. This is the// only XMP if everything fits within the 64KB limit for marker segment data. If not, there will be// a series of XMP extension segments.//// Each XMP extension segment is an APP1 marker segment whose data contains:// - A 35 byte signature string of "http://ns.adobe.com/xmp/extension/\0".// - A 128 bit GUID stored as 32 ASCII hex digits, capital A-F, no nul termination.// - A 32 bit unsigned integer length for the full extended XMP serialization.// - A 32 bit unsigned integer offset for this portion of the extended XMP serialization.// - A portion of the extended XMP serialization, up to about 65400 bytes (at most 65458).//// A reader must be prepared to encounter the extended XMP portions out of order. Also to encounter// defective files that have differing extended XMP according to the GUID. The main XMP contains the// GUID for the associated extended XMP.// *** This implementation simply returns when invalid JPEG is encountered. Should we throw instead?void JPEG_MetaHandler::CacheFileData(){ LFA_FileRef fileRef = this->parent->fileRef; XMP_PacketInfo & packetInfo = this->packetInfo; size_t segLen; bool ok; IOBuffer ioBuf; XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; const bool checkAbort = (abortProc != 0); ExtendedXMPInfo extXMP; XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) ); // Set containsXMP to true here only if the standard XMP packet is found. XMP_Assert ( kPSIRSignatureLength == (strlen(kPSIRSignatureString) + 1) ); XMP_Assert ( kMainXMPSignatureLength == (strlen(kMainXMPSignatureString) + 1) ); XMP_Assert ( kExtXMPSignatureLength == (strlen(kExtXMPSignatureString) + 1) ); // ------------------------------------------------------------------------------------------- // Look for any of the Exif, PSIR, main XMP, or extended XMP marker segments. Quit when we hit // an SOFn, EOI, or invalid/unexpected marker. LFA_Seek ( fileRef, 2, SEEK_SET ); // Skip the SOI. The JPEG header has already been verified. ioBuf.filePos = 2; RefillBuffer ( fileRef, &ioBuf ); while ( true ) { if ( checkAbort && abortProc(abortArg) ) { XMP_Throw ( "JPEG_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); } if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; if ( *ioBuf.ptr != 0xFF ) return; // All valid markers have a high byte of 0xFF. while ( *ioBuf.ptr == 0xFF ) { // Skip padding 0xFF bytes and the marker's high byte. ++ioBuf.ptr; if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return; } XMP_Uns16 marker = 0xFF00 + *ioBuf.ptr; if ( marker == 0xFFED ) { // This is an APP13 marker, is it the Photoshop image resources? ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field. if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; segLen = GetUns16BE ( ioBuf.ptr ); if ( segLen < 2 ) return; // Invalid JPEG. ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content. segLen -= 2; // Adjust segLen to count just the content portion. ok = CheckFileSpace ( fileRef, &ioBuf, kPSIRSignatureLength ); if ( ok && (segLen >= kPSIRSignatureLength) && CheckBytes ( ioBuf.ptr, kPSIRSignatureString, kPSIRSignatureLength ) ) { // This is the Photoshop image resources, cache the contents. ioBuf.ptr += kPSIRSignatureLength; // Move ioBuf.ptr to the image resources. segLen -= kPSIRSignatureLength; // Adjust segLen to count just the image resources. ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. if ( ! ok ) return; // Must be a truncated file. this->psirContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen ); ioBuf.ptr += segLen; } else { // This is the not Photoshop image resources, skip the marker segment's content. if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) { ioBuf.ptr += segLen; // The next marker is in this buffer. } else { // The next marker is beyond this buffer, RefillBuffer assumes we're doing sequential reads. size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer. ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR ); ioBuf.ptr = ioBuf.limit; // No data left in the buffer. } } continue; // Move on to the next marker. } else if ( marker == 0xFFE1 ) { // This is an APP1 marker, is it the Exif, main XMP, or extended XMP? // ! Check in that order, which happens to be increasing signature string length. ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field. if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; segLen = GetUns16BE ( ioBuf.ptr ); if ( segLen < 2 ) return; // Invalid JPEG. ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content. segLen -= 2; // Adjust segLen to count just the content portion. // Check for the Exif APP1 marker segment. ok = CheckFileSpace ( fileRef, &ioBuf, kExifSignatureLength ); if ( ok && (segLen >= kExifSignatureLength) && (CheckBytes ( ioBuf.ptr, kExifSignatureString, kExifSignatureLength ) || CheckBytes ( ioBuf.ptr, kExifSignatureAltStr, kExifSignatureLength )) ) { // This is the Exif metadata, cache the contents. ioBuf.ptr += kExifSignatureLength; // Move ioBuf.ptr to the TIFF stream. segLen -= kExifSignatureLength; // Adjust segLen to count just the TIFF stream. ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. if ( ! ok ) return; // Must be a truncated file.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -