Blame src/psdimage.cpp

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