|
Packit |
01d647 |
// ***************************************************************** -*- C++ -*-
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
* Copyright (C) 2004-2018 Exiv2 authors
|
|
Packit |
01d647 |
* This program is part of the Exiv2 distribution.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
01d647 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
01d647 |
* as published by the Free Software Foundation; either version 2
|
|
Packit |
01d647 |
* of the License, or (at your option) any later version.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
01d647 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
01d647 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
01d647 |
* GNU General Public License for more details.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
01d647 |
* along with this program; if not, write to the Free Software
|
|
Packit |
01d647 |
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
File: psdimage.cpp
|
|
Packit |
01d647 |
Author(s): Marco Piovanelli, Ovolab (marco)
|
|
Packit |
01d647 |
Michael Ulbrich (mul)
|
|
Packit |
01d647 |
History: 05-Mar-2007, marco: created
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
// included header files
|
|
Packit |
01d647 |
#include "config.h"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#include "psdimage.hpp"
|
|
Packit |
01d647 |
#include "jpgimage.hpp"
|
|
Packit |
01d647 |
#include "image.hpp"
|
|
Packit |
01d647 |
#include "basicio.hpp"
|
|
Packit |
01d647 |
#include "error.hpp"
|
|
Packit |
01d647 |
#include "futils.hpp"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#include "safe_op.hpp"
|
|
Packit |
01d647 |
#include "enforce.hpp"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// + standard includes
|
|
Packit |
01d647 |
#include <string>
|
|
Packit |
01d647 |
#include <cstring>
|
|
Packit |
01d647 |
#include <iostream>
|
|
Packit |
01d647 |
#include <iomanip>
|
|
Packit |
01d647 |
#include <cassert>
|
|
Packit |
01d647 |
#include <memory>
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Todo: Consolidate with existing code in struct Photoshop (jpgimage.hpp):
|
|
Packit |
01d647 |
// Extend this helper to a proper class with all required functionality,
|
|
Packit |
01d647 |
// then move it here or into a separate file?
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! @cond IGNORE
|
|
Packit |
01d647 |
struct PhotoshopResourceBlock {
|
|
Packit |
01d647 |
uint32_t resourceType; // one of the markers in Photoshop::irbId_[]
|
|
Packit |
01d647 |
uint16_t resourceId;
|
|
Packit |
01d647 |
unsigned char resourceName[2]; // Pascal string (length byte + characters), padded to an even size -- this assumes the empty string
|
|
Packit |
01d647 |
uint32_t resourceDataSize;
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
//! @endcond
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Photoshop resource IDs (Cf. <http://search.cpan.org/~bettelli/Image-MetaData-JPEG-0.15/lib/Image/MetaData/JPEG/TagLists.pod>)
|
|
Packit |
01d647 |
enum {
|
|
Packit |
01d647 |
kPhotoshopResourceID_Photoshop2Info = 0x03e8, // [obsolete -- Photoshop 2.0 only] General information -- contains five 2-byte values: number of channels, rows, columns, depth and mode
|
|
Packit |
01d647 |
kPhotoshopResourceID_MacintoshClassicPrintInfo = 0x03e9, // [optional] Macintosh classic print record (120 bytes)
|
|
Packit |
01d647 |
kPhotoshopResourceID_MacintoshCarbonPrintInfo = 0x03ea, // [optional] Macintosh carbon print info (variable-length XML format)
|
|
Packit |
01d647 |
kPhotoshopResourceID_Photoshop2ColorTable = 0x03eb, // [obsolete -- Photoshop 2.0 only] Indexed color table
|
|
Packit |
01d647 |
kPhotoshopResourceID_ResolutionInfo = 0x03ed, // PhotoshopResolutionInfo structure (see below)
|
|
Packit |
01d647 |
kPhotoshopResourceID_AlphaChannelsNames = 0x03ee, // as a series of Pstrings
|
|
Packit |
01d647 |
kPhotoshopResourceID_DisplayInfo = 0x03ef, // see appendix A in Photoshop SDK
|
|
Packit |
01d647 |
kPhotoshopResourceID_PStringCaption = 0x03f0, // [optional] the caption, as a Pstring
|
|
Packit |
01d647 |
kPhotoshopResourceID_BorderInformation = 0x03f1, // border width and units
|
|
Packit |
01d647 |
kPhotoshopResourceID_BackgroundColor = 0x03f2, // see additional Adobe information
|
|
Packit |
01d647 |
kPhotoshopResourceID_PrintFlags = 0x03f3, // labels, crop marks, colour bars, ecc...
|
|
Packit |
01d647 |
kPhotoshopResourceID_BWHalftoningInfo = 0x03f4, // Gray-scale and multich. half-toning info
|
|
Packit |
01d647 |
kPhotoshopResourceID_ColorHalftoningInfo = 0x03f5, // Colour half-toning information
|
|
Packit |
01d647 |
kPhotoshopResourceID_DuotoneHalftoningInfo = 0x03f6, // Duo-tone half-toning information
|
|
Packit |
01d647 |
kPhotoshopResourceID_BWTransferFunc = 0x03f7, // Gray-scale and multich. transfer function
|
|
Packit |
01d647 |
kPhotoshopResourceID_ColorTransferFuncs = 0x03f8, // Colour transfer function
|
|
Packit |
01d647 |
kPhotoshopResourceID_DuotoneTransferFuncs = 0x03f9, // Duo-tone transfer function
|
|
Packit |
01d647 |
kPhotoshopResourceID_DuotoneImageInfo = 0x03fa, // Duo-tone image information
|
|
Packit |
01d647 |
kPhotoshopResourceID_EffectiveBW = 0x03fb, // two bytes for the effective black and white values
|
|
Packit |
01d647 |
kPhotoshopResourceID_ObsoletePhotoshopTag1 = 0x03fc, // [obsolete]
|
|
Packit |
01d647 |
kPhotoshopResourceID_EPSOptions = 0x03fd, // Encapsulated Postscript options
|
|
Packit |
01d647 |
kPhotoshopResourceID_QuickMaskInfo = 0x03fe, // Quick Mask information. 2 bytes containing Quick Mask channel ID, 1 byte boolean indicating whether the mask was initially empty.
|
|
Packit |
01d647 |
kPhotoshopResourceID_ObsoletePhotoshopTag2 = 0x03ff, // [obsolete]
|
|
Packit |
01d647 |
kPhotoshopResourceID_LayerStateInfo = 0x0400, // index of target layer (0 means bottom)
|
|
Packit |
01d647 |
kPhotoshopResourceID_WorkingPathInfo = 0x0401, // should not be saved to the file
|
|
Packit |
01d647 |
kPhotoshopResourceID_LayersGroupInfo = 0x0402, // for grouping layers together
|
|
Packit |
01d647 |
kPhotoshopResourceID_ObsoletePhotoshopTag3 = 0x0403, // [obsolete] ??
|
|
Packit |
01d647 |
kPhotoshopResourceID_IPTC_NAA = 0x0404, // IPTC/NAA data
|
|
Packit |
01d647 |
kPhotoshopResourceID_RawImageMode = 0x0405, // image mode for raw format files
|
|
Packit |
01d647 |
kPhotoshopResourceID_JPEGQuality = 0x0406, // [private]
|
|
Packit |
01d647 |
kPhotoshopResourceID_GridGuidesInfo = 0x0408, // see additional Adobe information
|
|
Packit |
01d647 |
kPhotoshopResourceID_ThumbnailResource = 0x0409, // see additional Adobe information
|
|
Packit |
01d647 |
kPhotoshopResourceID_CopyrightFlag = 0x040a, // true if image is copyrighted
|
|
Packit |
01d647 |
kPhotoshopResourceID_URL = 0x040b, // text string with a resource locator
|
|
Packit |
01d647 |
kPhotoshopResourceID_ThumbnailResource2 = 0x040c, // see additional Adobe information
|
|
Packit |
01d647 |
kPhotoshopResourceID_GlobalAngle = 0x040d, // global lighting angle for effects layer
|
|
Packit |
01d647 |
kPhotoshopResourceID_ColorSamplersResource = 0x040e, // see additional Adobe information
|
|
Packit |
01d647 |
kPhotoshopResourceID_ICCProfile = 0x040f, // see notes from Internat. Color Consortium
|
|
Packit |
01d647 |
kPhotoshopResourceID_Watermark = 0x0410, // one byte
|
|
Packit |
01d647 |
kPhotoshopResourceID_ICCUntagged = 0x0411, // 1 means intentionally untagged
|
|
Packit |
01d647 |
kPhotoshopResourceID_EffectsVisible = 0x0412, // 1 byte to show/hide all effects layers
|
|
Packit |
01d647 |
kPhotoshopResourceID_SpotHalftone = 0x0413, // version, length and data
|
|
Packit |
01d647 |
kPhotoshopResourceID_IDsBaseValue = 0x0414, // base value for new layers ID's
|
|
Packit |
01d647 |
kPhotoshopResourceID_UnicodeAlphaNames = 0x0415, // length plus Unicode string
|
|
Packit |
01d647 |
kPhotoshopResourceID_IndexedColourTableCount = 0x0416, // [Photoshop 6.0 and later] 2 bytes
|
|
Packit |
01d647 |
kPhotoshopResourceID_TransparentIndex = 0x0417, // [Photoshop 6.0 and later] 2 bytes
|
|
Packit |
01d647 |
kPhotoshopResourceID_GlobalAltitude = 0x0419, // [Photoshop 6.0 and later] 4 bytes
|
|
Packit |
01d647 |
kPhotoshopResourceID_Slices = 0x041a, // [Photoshop 6.0 and later] see additional Adobe info
|
|
Packit |
01d647 |
kPhotoshopResourceID_WorkflowURL = 0x041b, // [Photoshop 6.0 and later] 4 bytes length + Unicode string
|
|
Packit |
01d647 |
kPhotoshopResourceID_JumpToXPEP = 0x041c, // [Photoshop 6.0 and later] see additional Adobe info
|
|
Packit |
01d647 |
kPhotoshopResourceID_AlphaIdentifiers = 0x041d, // [Photoshop 6.0 and later] 4*(n+1) bytes
|
|
Packit |
01d647 |
kPhotoshopResourceID_URLList = 0x041e, // [Photoshop 6.0 and later] structured Unicode URL's
|
|
Packit |
01d647 |
kPhotoshopResourceID_VersionInfo = 0x0421, // [Photoshop 6.0 and later] see additional Adobe info
|
|
Packit |
01d647 |
kPhotoshopResourceID_ExifInfo = 0x0422, // [Photoshop 7.0?] Exif metadata
|
|
Packit |
01d647 |
kPhotoshopResourceID_XMPPacket = 0x0424, // [Photoshop 7.0?] XMP packet -- see http://www.adobe.com/devnet/xmp/pdfs/xmp_specification.pdf
|
|
Packit |
01d647 |
kPhotoshopResourceID_ClippingPathName = 0x0bb7, // [Photoshop 6.0 and later] name of clipping path
|
|
Packit |
01d647 |
kPhotoshopResourceID_MorePrintFlags = 0x2710 // [Photoshop 6.0 and later] Print flags information. 2 bytes version (=1), 1 byte center crop marks, 1 byte (=0), 4 bytes bleed width value, 2 bytes bleed width scale.
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
// class member definitions
|
|
Packit |
01d647 |
namespace Exiv2 {
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
PsdImage::PsdImage(BasicIo::AutoPtr io)
|
|
Packit |
01d647 |
: Image(ImageType::psd, mdExif | mdIptc | mdXmp, io)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
} // PsdImage::PsdImage
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::string PsdImage::mimeType() const
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return "image/x-photoshop";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void PsdImage::setComment(const std::string& /*comment*/)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// not supported
|
|
Packit |
01d647 |
throw(Error(kerInvalidSettingForImage, "Image comment", "Photoshop"));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void PsdImage::readMetadata()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << "Exiv2::PsdImage::readMetadata: Reading Photoshop file " << io_->path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (io_->open() != 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerDataSourceOpenFailed, io_->path(), strError());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
IoCloser closer(*io_);
|
|
Packit |
01d647 |
// Ensure that this is the correct image type
|
|
Packit |
01d647 |
if (!isPsdType(*io_, false))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
clearMetadata();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
The Photoshop header goes as follows -- all numbers are in big-endian byte order:
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
offset length name description
|
|
Packit |
01d647 |
====== ======= ========= =========
|
|
Packit |
01d647 |
0 4 bytes signature always '8BPS'
|
|
Packit |
01d647 |
4 2 bytes version always equal to 1
|
|
Packit |
01d647 |
6 6 bytes reserved must be zero
|
|
Packit |
01d647 |
12 2 bytes channels number of channels in the image, including alpha channels (1 to 24)
|
|
Packit |
01d647 |
14 4 bytes rows the height of the image in pixels
|
|
Packit |
01d647 |
18 4 bytes columns the width of the image in pixels
|
|
Packit |
01d647 |
22 2 bytes depth the number of bits per channel
|
|
Packit |
01d647 |
24 2 bytes mode the color mode of the file; Supported values are: Bitmap=0; Grayscale=1; Indexed=2; RGB=3; CMYK=4; Multichannel=7; Duotone=8; Lab=9
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
byte buf[26];
|
|
Packit |
01d647 |
if (io_->read(buf, 26) != 26)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
pixelWidth_ = getLong(buf + 18, bigEndian);
|
|
Packit |
01d647 |
pixelHeight_ = getLong(buf + 14, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// immediately following the image header is the color mode data section,
|
|
Packit |
01d647 |
// the first four bytes of which specify the byte size of the whole section
|
|
Packit |
01d647 |
if (io_->read(buf, 4) != 4)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// skip it
|
|
Packit |
01d647 |
uint32_t colorDataLength = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
if (io_->seek(colorDataLength, BasicIo::cur))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// after the color data section, comes a list of resource blocks, preceded by the total byte size
|
|
Packit |
01d647 |
if (io_->read(buf, 4) != 4)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
uint32_t resourcesLength = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
enforce(resourcesLength < io_->size(), Exiv2::kerCorruptedMetadata);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while (resourcesLength > 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
enforce(resourcesLength >= 8, Exiv2::kerCorruptedMetadata);
|
|
Packit |
01d647 |
resourcesLength -= 8;
|
|
Packit |
01d647 |
if (io_->read(buf, 8) != 8)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (!Photoshop::isIrb(buf, 4))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
break; // bad resource type
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
uint16_t resourceId = getUShort(buf + 4, bigEndian);
|
|
Packit |
01d647 |
uint32_t resourceNameLength = buf[6] & ~1;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// skip the resource name, plus any padding
|
|
Packit |
01d647 |
enforce(resourceNameLength <= resourcesLength, Exiv2::kerCorruptedMetadata);
|
|
Packit |
01d647 |
resourcesLength -= resourceNameLength;
|
|
Packit |
01d647 |
io_->seek(resourceNameLength, BasicIo::cur);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// read resource size
|
|
Packit |
01d647 |
enforce(resourcesLength >= 4, Exiv2::kerCorruptedMetadata);
|
|
Packit |
01d647 |
resourcesLength -= 4;
|
|
Packit |
01d647 |
if (io_->read(buf, 4) != 4)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
uint32_t resourceSize = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
uint32_t curOffset = io_->tell();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::hex << "resourceId: " << resourceId << std::dec << " length: " << resourceSize << std::hex << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
enforce(resourceSize <= resourcesLength, Exiv2::kerCorruptedMetadata);
|
|
Packit |
01d647 |
readResourceBlock(resourceId, resourceSize);
|
|
Packit |
01d647 |
resourceSize = (resourceSize + 1) & ~1; // pad to even
|
|
Packit |
01d647 |
enforce(resourceSize <= resourcesLength, Exiv2::kerCorruptedMetadata);
|
|
Packit |
01d647 |
resourcesLength -= resourceSize;
|
|
Packit |
01d647 |
io_->seek(curOffset + resourceSize, BasicIo::beg);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // PsdImage::readMetadata
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void PsdImage::readResourceBlock(uint16_t resourceId, uint32_t resourceSize)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
switch(resourceId)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
case kPhotoshopResourceID_IPTC_NAA:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf rawIPTC(resourceSize);
|
|
Packit |
01d647 |
io_->read(rawIPTC.pData_, rawIPTC.size_);
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (IptcParser::decode(iptcData_, rawIPTC.pData_, rawIPTC.size_)) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode IPTC metadata.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
iptcData_.clear();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
case kPhotoshopResourceID_ExifInfo:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf rawExif(resourceSize);
|
|
Packit |
01d647 |
io_->read(rawExif.pData_, rawExif.size_);
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
ByteOrder bo = ExifParser::decode(exifData_, rawExif.pData_, rawExif.size_);
|
|
Packit |
01d647 |
setByteOrder(bo);
|
|
Packit |
01d647 |
if (rawExif.size_ > 0 && byteOrder() == invalidByteOrder) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode Exif metadata.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
exifData_.clear();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
case kPhotoshopResourceID_XMPPacket:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf xmpPacket(resourceSize);
|
|
Packit |
01d647 |
io_->read(xmpPacket.pData_, xmpPacket.size_);
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
xmpPacket_.assign(reinterpret_cast<char *>(xmpPacket.pData_), xmpPacket.size_);
|
|
Packit |
01d647 |
if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode XMP metadata.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// - PS 4.0 preview data is fetched from ThumbnailResource
|
|
Packit |
01d647 |
// - PS >= 5.0 preview data is fetched from ThumbnailResource2
|
|
Packit |
01d647 |
case kPhotoshopResourceID_ThumbnailResource:
|
|
Packit |
01d647 |
case kPhotoshopResourceID_ThumbnailResource2:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
Photoshop thumbnail resource header
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
offset length name description
|
|
Packit |
01d647 |
====== ======== ==== ===========
|
|
Packit |
01d647 |
0 4 bytes format = 1 (kJpegRGB). Also supports kRawRGB (0).
|
|
Packit |
01d647 |
4 4 bytes width Width of thumbnail in pixels.
|
|
Packit |
01d647 |
8 4 bytes height Height of thumbnail in pixels.
|
|
Packit |
01d647 |
12 4 bytes widthbytes Padded row bytes as (width * bitspixel + 31) / 32 * 4.
|
|
Packit |
01d647 |
16 4 bytes size Total size as widthbytes * height * planes
|
|
Packit |
01d647 |
20 4 bytes compressedsize Size after compression. Used for consistentcy check.
|
|
Packit |
01d647 |
24 2 bytes bitspixel = 24. Bits per pixel.
|
|
Packit |
01d647 |
26 2 bytes planes = 1. Number of planes.
|
|
Packit |
01d647 |
28 variable data JFIF data in RGB format.
|
|
Packit |
01d647 |
Note: For resource ID 1033 the data is in BGR format.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
byte buf[28];
|
|
Packit |
01d647 |
if (io_->read(buf, 28) != 28)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
NativePreview nativePreview;
|
|
Packit |
01d647 |
nativePreview.position_ = io_->tell();
|
|
Packit |
01d647 |
nativePreview.size_ = getLong(buf + 20, bigEndian); // compressedsize
|
|
Packit |
01d647 |
nativePreview.width_ = getLong(buf + 4, bigEndian);
|
|
Packit |
01d647 |
nativePreview.height_ = getLong(buf + 8, bigEndian);
|
|
Packit |
01d647 |
const uint32_t format = getLong(buf + 0, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (nativePreview.size_ > 0 && nativePreview.position_ >= 0) {
|
|
Packit |
01d647 |
io_->seek(static_cast<long>(nativePreview.size_), BasicIo::cur);
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (format == 1) {
|
|
Packit |
01d647 |
nativePreview.filter_ = "";
|
|
Packit |
01d647 |
nativePreview.mimeType_ = "image/jpeg";
|
|
Packit |
01d647 |
nativePreviews_.push_back(nativePreview);
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
// unsupported format of native preview
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
default:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} // PsdImage::readResourceBlock
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void PsdImage::writeMetadata()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->open() != 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerDataSourceOpenFailed, io_->path(), strError());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
IoCloser closer(*io_);
|
|
Packit |
01d647 |
BasicIo::AutoPtr tempIo(new MemIo);
|
|
Packit |
01d647 |
assert (tempIo.get() != 0);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
doWriteMetadata(*tempIo); // may throw
|
|
Packit |
01d647 |
io_->close();
|
|
Packit |
01d647 |
io_->transfer(*tempIo); // may throw
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // PsdImage::writeMetadata
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void PsdImage::doWriteMetadata(BasicIo& outIo)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (!io_->isopen()) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
if (!outIo.isopen()) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::PsdImage::doWriteMetadata: Writing PSD file " << io_->path() << "\n";
|
|
Packit |
01d647 |
std::cout << "Exiv2::PsdImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Ensure that this is the correct image type
|
|
Packit |
01d647 |
if (!isPsdType(*io_, true)) {
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
throw Error(kerNoImageInInputData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
io_->seek(0, BasicIo::beg); // rewind
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf lbuf(4096);
|
|
Packit |
01d647 |
byte buf[8];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Get Photoshop header from original file
|
|
Packit |
01d647 |
byte psd_head[26];
|
|
Packit |
01d647 |
if (io_->read(psd_head, 26) != 26) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write Photoshop header data out to new PSD file
|
|
Packit |
01d647 |
if (outIo.write(psd_head, 26) != 26) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Read colorDataLength from original PSD
|
|
Packit |
01d647 |
if (io_->read(buf, 4) != 4) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t colorDataLength = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write colorDataLength
|
|
Packit |
01d647 |
ul2Data(buf, colorDataLength, bigEndian);
|
|
Packit |
01d647 |
if (outIo.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::dec << "colorDataLength: " << colorDataLength << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
// Copy colorData
|
|
Packit |
01d647 |
uint32_t readTotal = 0;
|
|
Packit |
01d647 |
long toRead = 0;
|
|
Packit |
01d647 |
while (readTotal < colorDataLength) {
|
|
Packit |
01d647 |
toRead = static_cast<long>(colorDataLength - readTotal) < lbuf.size_
|
|
Packit |
01d647 |
? static_cast<long>(colorDataLength - readTotal) : lbuf.size_;
|
|
Packit |
01d647 |
if (io_->read(lbuf.pData_, toRead) != toRead) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
readTotal += toRead;
|
|
Packit |
01d647 |
if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (outIo.error()) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t resLenOffset = io_->tell(); // remember for later update
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Read length of all resource blocks from original PSD
|
|
Packit |
01d647 |
if (io_->read(buf, 4) != 4) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t oldResLength = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
uint32_t newResLength = 0;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write oldResLength (will be updated later)
|
|
Packit |
01d647 |
ul2Data(buf, oldResLength, bigEndian);
|
|
Packit |
01d647 |
if (outIo.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::dec << "oldResLength: " << oldResLength << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Iterate over original resource blocks.
|
|
Packit |
01d647 |
// Replace or insert IPTC, EXIF and XMP
|
|
Packit |
01d647 |
// Original resource blocks assumed to be sorted ASC
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool iptcDone = false;
|
|
Packit |
01d647 |
bool exifDone = false;
|
|
Packit |
01d647 |
bool xmpDone = false;
|
|
Packit |
01d647 |
while (oldResLength > 0) {
|
|
Packit |
01d647 |
if (io_->read(buf, 8) != 8) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// read resource type and ID
|
|
Packit |
01d647 |
uint32_t resourceType = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (!Photoshop::isIrb(buf, 4))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop"); // bad resource type
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
uint16_t resourceId = getUShort(buf + 4, bigEndian);
|
|
Packit |
01d647 |
uint32_t resourceNameLength = buf[6];
|
|
Packit |
01d647 |
uint32_t adjResourceNameLen = resourceNameLength & ~1;
|
|
Packit |
01d647 |
unsigned char resourceNameFirstChar = buf[7];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// read rest of resource name, plus any padding
|
|
Packit |
01d647 |
DataBuf resName(256);
|
|
Packit |
01d647 |
if ( io_->read(resName.pData_, adjResourceNameLen)
|
|
Packit |
01d647 |
!= static_cast<long>(adjResourceNameLen)) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// read resource size (actual length w/o padding!)
|
|
Packit |
01d647 |
if (io_->read(buf, 4) != 4) throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t resourceSize = getULong(buf, bigEndian);
|
|
Packit |
01d647 |
uint32_t pResourceSize = (resourceSize + 1) & ~1; // padded resource size
|
|
Packit |
01d647 |
uint32_t curOffset = io_->tell();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write IPTC_NAA resource block
|
|
Packit |
01d647 |
if ((resourceId == kPhotoshopResourceID_IPTC_NAA ||
|
|
Packit |
01d647 |
resourceId > kPhotoshopResourceID_IPTC_NAA) && iptcDone == false) {
|
|
Packit |
01d647 |
newResLength += writeIptcData(iptcData_, outIo);
|
|
Packit |
01d647 |
iptcDone = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write ExifInfo resource block
|
|
Packit |
01d647 |
else if ((resourceId == kPhotoshopResourceID_ExifInfo ||
|
|
Packit |
01d647 |
resourceId > kPhotoshopResourceID_ExifInfo) && exifDone == false) {
|
|
Packit |
01d647 |
newResLength += writeExifData(exifData_, outIo);
|
|
Packit |
01d647 |
exifDone = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write XMPpacket resource block
|
|
Packit |
01d647 |
else if ((resourceId == kPhotoshopResourceID_XMPPacket ||
|
|
Packit |
01d647 |
resourceId > kPhotoshopResourceID_XMPPacket) && xmpDone == false) {
|
|
Packit |
01d647 |
newResLength += writeXmpData(xmpData_, outIo);
|
|
Packit |
01d647 |
xmpDone = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Copy all other resource blocks
|
|
Packit |
01d647 |
if ( resourceId != kPhotoshopResourceID_IPTC_NAA
|
|
Packit |
01d647 |
&& resourceId != kPhotoshopResourceID_ExifInfo
|
|
Packit |
01d647 |
&& resourceId != kPhotoshopResourceID_XMPPacket) {
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::hex << "copy : resourceType: " << resourceType << "\n";
|
|
Packit |
01d647 |
std::cerr << std::hex << "copy : resourceId: " << resourceId << "\n";
|
|
Packit |
01d647 |
std::cerr << std::dec;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
// Copy resource block to new PSD file
|
|
Packit |
01d647 |
ul2Data(buf, resourceType, bigEndian);
|
|
Packit |
01d647 |
if (outIo.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, resourceId, bigEndian);
|
|
Packit |
01d647 |
if (outIo.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
// Write resource name as Pascal string
|
|
Packit |
01d647 |
buf[0] = resourceNameLength & 0x00ff;
|
|
Packit |
01d647 |
if (outIo.write(buf, 1) != 1) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
buf[0] = resourceNameFirstChar;
|
|
Packit |
01d647 |
if (outIo.write(buf, 1) != 1) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
if ( outIo.write(resName.pData_, adjResourceNameLen)
|
|
Packit |
01d647 |
!= static_cast<long>(adjResourceNameLen)) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
ul2Data(buf, resourceSize, bigEndian);
|
|
Packit |
01d647 |
if (outIo.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
readTotal = 0;
|
|
Packit |
01d647 |
toRead = 0;
|
|
Packit |
01d647 |
while (readTotal < pResourceSize) {
|
|
Packit |
01d647 |
toRead = static_cast<long>(pResourceSize - readTotal) < lbuf.size_
|
|
Packit |
01d647 |
? static_cast<long>(pResourceSize - readTotal) : lbuf.size_;
|
|
Packit |
01d647 |
if (io_->read(lbuf.pData_, toRead) != toRead) {
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "Photoshop");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
readTotal += toRead;
|
|
Packit |
01d647 |
if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (outIo.error()) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
newResLength += pResourceSize + adjResourceNameLen + 12;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
io_->seek(curOffset + pResourceSize, BasicIo::beg);
|
|
Packit |
01d647 |
oldResLength -= (12 + adjResourceNameLen + pResourceSize);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Append IPTC_NAA resource block, if not yet written
|
|
Packit |
01d647 |
if (iptcDone == false) {
|
|
Packit |
01d647 |
newResLength += writeIptcData(iptcData_, outIo);
|
|
Packit |
01d647 |
iptcDone = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Append ExifInfo resource block, if not yet written
|
|
Packit |
01d647 |
if (exifDone == false) {
|
|
Packit |
01d647 |
newResLength += writeExifData(exifData_, outIo);
|
|
Packit |
01d647 |
exifDone = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Append XmpPacket resource block, if not yet written
|
|
Packit |
01d647 |
if (xmpDone == false) {
|
|
Packit |
01d647 |
newResLength += writeXmpData(xmpData_, outIo);
|
|
Packit |
01d647 |
xmpDone = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Populate the fake data, only make sense for remoteio, httpio and sshio.
|
|
Packit |
01d647 |
// it avoids allocating memory for parts of the file that contain image-date.
|
|
Packit |
01d647 |
io_->populateFakeData();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Copy remaining data
|
|
Packit |
01d647 |
long readSize = 0;
|
|
Packit |
01d647 |
while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) {
|
|
Packit |
01d647 |
if (outIo.write(lbuf.pData_, readSize) != readSize) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (outIo.error()) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Update length of resources
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << "newResLength: " << newResLength << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
outIo.seek(resLenOffset, BasicIo::beg);
|
|
Packit |
01d647 |
ul2Data(buf, newResLength, bigEndian);
|
|
Packit |
01d647 |
if (outIo.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // PsdImage::doWriteMetadata
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t PsdImage::writeIptcData(const IptcData& iptcData, BasicIo& out) const
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
uint32_t resLength = 0;
|
|
Packit |
01d647 |
byte buf[8];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (iptcData.count() > 0) {
|
|
Packit |
01d647 |
DataBuf rawIptc = IptcParser::encode(iptcData);
|
|
Packit |
01d647 |
if (rawIptc.size_ > 0) {
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_IPTC_NAA << "\n";
|
|
Packit |
01d647 |
std::cerr << std::dec << "Writing IPTC_NAA: size: " << rawIptc.size_ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (out.write(reinterpret_cast<const byte*>(Photoshop::irbId_[0]), 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, kPhotoshopResourceID_IPTC_NAA, bigEndian);
|
|
Packit |
01d647 |
if (out.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, 0, bigEndian); // NULL resource name
|
|
Packit |
01d647 |
if (out.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
ul2Data(buf, rawIptc.size_, bigEndian);
|
|
Packit |
01d647 |
if (out.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
// Write encoded Iptc data
|
|
Packit |
01d647 |
if (out.write(rawIptc.pData_, rawIptc.size_) != rawIptc.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
resLength += rawIptc.size_ + 12;
|
|
Packit |
01d647 |
if (rawIptc.size_ & 1) // even padding
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
buf[0] = 0;
|
|
Packit |
01d647 |
if (out.write(buf, 1) != 1) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
resLength++;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return resLength;
|
|
Packit |
01d647 |
} // PsdImage::writeIptcData
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t PsdImage::writeExifData(const ExifData& exifData, BasicIo& out)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
uint32_t resLength = 0;
|
|
Packit |
01d647 |
byte buf[8];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (exifData.count() > 0) {
|
|
Packit |
01d647 |
Blob blob;
|
|
Packit |
01d647 |
ByteOrder bo = byteOrder();
|
|
Packit |
01d647 |
if (bo == invalidByteOrder) {
|
|
Packit |
01d647 |
bo = littleEndian;
|
|
Packit |
01d647 |
setByteOrder(bo);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
ExifParser::encode(blob, bo, exifData);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (blob.size() > 0) {
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_ExifInfo << "\n";
|
|
Packit |
01d647 |
std::cerr << std::dec << "Writing ExifInfo: size: " << blob.size() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (out.write(reinterpret_cast<const byte*>(Photoshop::irbId_[0]), 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, kPhotoshopResourceID_ExifInfo, bigEndian);
|
|
Packit |
01d647 |
if (out.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, 0, bigEndian); // NULL resource name
|
|
Packit |
01d647 |
if (out.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
ul2Data(buf, static_cast<uint32_t>(blob.size()), bigEndian);
|
|
Packit |
01d647 |
if (out.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
// Write encoded Exif data
|
|
Packit |
01d647 |
if (out.write(&blob[0], static_cast<long>(blob.size())) != static_cast<long>(blob.size())) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
resLength += static_cast<long>(blob.size()) + 12;
|
|
Packit |
01d647 |
if (blob.size() & 1) // even padding
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
buf[0] = 0;
|
|
Packit |
01d647 |
if (out.write(buf, 1) != 1) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
resLength++;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return resLength;
|
|
Packit |
01d647 |
} // PsdImage::writeExifData
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
uint32_t PsdImage::writeXmpData(const XmpData& xmpData, BasicIo& out) const
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
std::string xmpPacket;
|
|
Packit |
01d647 |
uint32_t resLength = 0;
|
|
Packit |
01d647 |
byte buf[8];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << "writeXmpFromPacket(): " << writeXmpFromPacket() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
// writeXmpFromPacket(true);
|
|
Packit |
01d647 |
if (writeXmpFromPacket() == false) {
|
|
Packit |
01d647 |
if (XmpParser::encode(xmpPacket, xmpData) > 1) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_ERROR << "Failed to encode XMP metadata.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (xmpPacket.size() > 0) {
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_XMPPacket << "\n";
|
|
Packit |
01d647 |
std::cerr << std::dec << "Writing XMPPacket: size: " << xmpPacket.size() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (out.write(reinterpret_cast<const byte*>(Photoshop::irbId_[0]), 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, kPhotoshopResourceID_XMPPacket, bigEndian);
|
|
Packit |
01d647 |
if (out.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
us2Data(buf, 0, bigEndian); // NULL resource name
|
|
Packit |
01d647 |
if (out.write(buf, 2) != 2) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
ul2Data(buf, static_cast<uint32_t>(xmpPacket.size()), bigEndian);
|
|
Packit |
01d647 |
if (out.write(buf, 4) != 4) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
// Write XMPPacket
|
|
Packit |
01d647 |
if (out.write(reinterpret_cast<const byte*>(xmpPacket.data()), static_cast<long>(xmpPacket.size()))
|
|
Packit |
01d647 |
!= static_cast<long>(xmpPacket.size())) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
if (out.error()) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
resLength += static_cast<uint32_t>(xmpPacket.size()) + 12;
|
|
Packit |
01d647 |
if (xmpPacket.size() & 1) // even padding
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
buf[0] = 0;
|
|
Packit |
01d647 |
if (out.write(buf, 1) != 1) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
resLength++;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return resLength;
|
|
Packit |
01d647 |
} // PsdImage::writeXmpData
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *************************************************************************
|
|
Packit |
01d647 |
// free functions
|
|
Packit |
01d647 |
Image::AutoPtr newPsdInstance(BasicIo::AutoPtr io, bool /*create*/)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
Image::AutoPtr image(new PsdImage(io));
|
|
Packit |
01d647 |
if (!image->good())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
image.reset();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return image;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool isPsdType(BasicIo& iIo, bool advance)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
const int32_t len = 6;
|
|
Packit |
01d647 |
const unsigned char PsdHeader[6] = { '8', 'B', 'P', 'S', 0, 1 };
|
|
Packit |
01d647 |
byte buf[len];
|
|
Packit |
01d647 |
iIo.read(buf, len);
|
|
Packit |
01d647 |
if (iIo.error() || iIo.eof())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
bool matched = (memcmp(buf, PsdHeader, len) == 0);
|
|
Packit |
01d647 |
if (!advance || !matched)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
iIo.seek(-len, BasicIo::cur);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
return matched;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} // namespace Exiv2
|