void PsdImage::readMetadata() { #ifdef DEBUG std::cerr << "Exiv2::PsdImage::readMetadata: Reading Photoshop file " << io_->path() << "\n"; #endif if (io_->open() != 0) { throw Error(9, io_->path(), strError()); } IoCloser closer(*io_); // Ensure that this is the correct image type if (!isPsdType(*io_, false)) { if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "Photoshop"); } clearMetadata(); /* The Photoshop header goes as follows -- all numbers are in big-endian byte order: offset length name description ====== ======= ========= ========= 0 4 bytes signature always '8BPS' 4 2 bytes version always equal to 1 6 6 bytes reserved must be zero 12 2 bytes channels number of channels in the image, including alpha channels (1 to 24) 14 4 bytes rows the height of the image in pixels 18 4 bytes columns the width of the image in pixels 22 2 bytes depth the number of bits per channel 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 */ byte buf[26]; if (io_->read(buf, 26) != 26) { throw Error(3, "Photoshop"); } pixelWidth_ = getLong(buf + 18, bigEndian); pixelHeight_ = getLong(buf + 14, bigEndian); // immediately following the image header is the color mode data section, // the first four bytes of which specify the byte size of the whole section if (io_->read(buf, 4) != 4) { throw Error(3, "Photoshop"); } // skip it uint32_t colorDataLength = getLong(buf, bigEndian); if (io_->seek(colorDataLength, BasicIo::cur)) { throw Error(3, "Photoshop"); } // after the color data section, comes a list of resource blocks, preceeded by the total byte size if (io_->read(buf, 4) != 4) { throw Error(3, "Photoshop"); } uint32_t resourcesLength = getLong(buf, bigEndian); while (resourcesLength > 0) { if (io_->read(buf, 8) != 8) { throw Error(3, "Photoshop"); } // read resource type and ID uint32_t resourceType = getLong(buf, bigEndian); uint16_t resourceId = getShort(buf + 4, bigEndian); if (resourceType != kPhotoshopResourceType) { break; // bad resource type } uint32_t resourceNameLength = buf[6] & ~1; // skip the resource name, plus any padding io_->seek(resourceNameLength, BasicIo::cur); // read resource size if (io_->read(buf, 4) != 4) { throw Error(3, "Photoshop"); } uint32_t resourceSize = getLong(buf, bigEndian); uint32_t curOffset = io_->tell(); processResourceBlock(resourceId, resourceSize); resourceSize = (resourceSize + 1) & ~1; // pad to even io_->seek(curOffset + resourceSize, BasicIo::beg); resourcesLength -= (12 + resourceNameLength + resourceSize); } } // PsdImage::readMetadata
void PsdImage::doWriteMetadata(BasicIo& outIo) { if (!io_->isopen()) throw Error(20); if (!outIo.isopen()) throw Error(21); #ifdef DEBUG std::cout << "Exiv2::PsdImage::doWriteMetadata: Writing PSD file " << io_->path() << "\n"; std::cout << "Exiv2::PsdImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; #endif // Ensure that this is the correct image type if (!isPsdType(*io_, true)) { if (io_->error() || io_->eof()) throw Error(20); throw Error(22); } io_->seek(0, BasicIo::beg); // rewind DataBuf lbuf(4096); byte buf[8]; // Get Photoshop header from original file byte psd_head[26]; if (io_->read(psd_head, 26) != 26) throw Error(3, "Photoshop"); // Write Photoshop header data out to new PSD file if (outIo.write(psd_head, 26) != 26) throw Error(21); // Read colorDataLength from original PSD if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); uint32_t colorDataLength = getULong(buf, bigEndian); // Write colorDataLength ul2Data(buf, colorDataLength, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); #ifdef DEBUG std::cerr << std::dec << "colorDataLength: " << colorDataLength << "\n"; #endif // Copy colorData uint32_t readTotal = 0; long toRead = 0; while (readTotal < colorDataLength) { toRead = static_cast<long>(colorDataLength - readTotal) < lbuf.size_ ? colorDataLength - readTotal : lbuf.size_; if (io_->read(lbuf.pData_, toRead) != toRead) throw Error(3, "Photoshop"); readTotal += toRead; if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21); } if (outIo.error()) throw Error(21); uint32_t resLenOffset = io_->tell(); // remember for later update // Read length of all resource blocks from original PSD if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); uint32_t oldResLength = getULong(buf, bigEndian); uint32_t newResLength = 0; // Write oldResLength (will be updated later) ul2Data(buf, oldResLength, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); #ifdef DEBUG std::cerr << std::dec << "oldResLength: " << oldResLength << "\n"; #endif // Iterate over original resource blocks. // Replace or insert IPTC, EXIF and XMP // Original resource blocks assumed to be sorted ASC bool iptcDone = false; bool exifDone = false; bool xmpDone = false; while (oldResLength > 0) { if (io_->read(buf, 8) != 8) throw Error(3, "Photoshop"); // read resource type and ID uint32_t resourceType = getULong(buf, bigEndian); if (resourceType != kPhotoshopResourceType) { break; // bad resource type } uint16_t resourceId = getUShort(buf + 4, bigEndian); uint32_t resourceNameLength = buf[6]; uint32_t adjResourceNameLen = resourceNameLength & ~1; unsigned char resourceNameFirstChar = buf[7]; // read rest of resource name, plus any padding DataBuf resName(256); if ( io_->read(resName.pData_, adjResourceNameLen) != static_cast<long>(adjResourceNameLen)) throw Error(3, "Photoshop"); // read resource size (actual length w/o padding!) if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop"); uint32_t resourceSize = getULong(buf, bigEndian); uint32_t curOffset = io_->tell(); // Write IPTC_NAA resource block if ( resourceId == kPhotoshopResourceID_IPTC_NAA || (resourceId > kPhotoshopResourceID_IPTC_NAA && iptcDone == false)) { newResLength += writeIptcData(iptcData_, outIo); resourceSize = (resourceSize + 1) & ~1; // adjust for padding iptcDone = true; } // Write ExifInfo resource block else if ( resourceId == kPhotoshopResourceID_ExifInfo || (resourceId > kPhotoshopResourceID_ExifInfo && exifDone == false)) { newResLength += writeExifData(exifData_, outIo); resourceSize = (resourceSize + 1) & ~1; // adjust for padding exifDone = true; } // Write XMPpacket resource block else if ( resourceId == kPhotoshopResourceID_XMPPacket || (resourceId > kPhotoshopResourceID_XMPPacket && xmpDone == false)) { newResLength += writeXmpData(xmpData_, outIo); resourceSize = (resourceSize + 1) & ~1; // adjust for padding xmpDone = true; } // Copy all other resource blocks if ( resourceId != kPhotoshopResourceID_IPTC_NAA && resourceId != kPhotoshopResourceID_ExifInfo && resourceId != kPhotoshopResourceID_XMPPacket) { #ifdef DEBUG std::cerr << std::hex << "copy : resourceId: " << resourceId << "\n"; std::cerr << std::dec; #endif // Copy resource block to new PSD file ul2Data(buf, kPhotoshopResourceType, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); us2Data(buf, resourceId, bigEndian); if (outIo.write(buf, 2) != 2) throw Error(21); // Write resource name as Pascal string buf[0] = resourceNameLength & 0x000f; if (outIo.write(buf, 1) != 1) throw Error(21); buf[0] = resourceNameFirstChar; if (outIo.write(buf, 1) != 1) throw Error(21); if ( outIo.write(resName.pData_, adjResourceNameLen) != static_cast<long>(adjResourceNameLen)) throw Error(21); ul2Data(buf, resourceSize, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); readTotal = 0; toRead = 0; resourceSize = (resourceSize + 1) & ~1; // pad to even while (readTotal < resourceSize) { toRead = static_cast<long>(resourceSize - readTotal) < lbuf.size_ ? resourceSize - readTotal : lbuf.size_; if (io_->read(lbuf.pData_, toRead) != toRead) { throw Error(3, "Photoshop"); } readTotal += toRead; if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21); } if (outIo.error()) throw Error(21); newResLength += resourceSize + adjResourceNameLen + 12; } io_->seek(curOffset + resourceSize, BasicIo::beg); oldResLength -= (12 + adjResourceNameLen + resourceSize); } // Append IPTC_NAA resource block, if not yet written if (iptcDone == false) { newResLength += writeIptcData(iptcData_, outIo); iptcDone = true; } // Append ExifInfo resource block, if not yet written if (exifDone == false) { newResLength += writeExifData(exifData_, outIo); exifDone = true; } // Append XmpPacket resource block, if not yet written if (xmpDone == false) { newResLength += writeXmpData(xmpData_, outIo); xmpDone = true; } // Copy remaining data long readSize = 0; while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) { if (outIo.write(lbuf.pData_, readSize) != readSize) throw Error(21); } if (outIo.error()) throw Error(21); // Update length of resources #ifdef DEBUG std::cerr << "newResLength: " << newResLength << "\n"; #endif outIo.seek(resLenOffset, BasicIo::beg); ul2Data(buf, newResLength, bigEndian); if (outIo.write(buf, 4) != 4) throw Error(21); } // PsdImage::doWriteMetadata