| 29 |
29 |
#include "rcsid.hpp"
|
| 30 |
30 |
EXIV2_RCSID("@(#) $Id: psdimage.cpp 1750 2009-02-16 14:30:51Z ahuggel $")
|
| 31 |
31 |
|
| 32 |
|
//#define DEBUG 1
|
|
32 |
#define DEBUG 0
|
| 33 |
33 |
|
| 34 |
34 |
// *****************************************************************************
|
| 35 |
35 |
// included header files
|
| ... | ... | |
| 143 |
143 |
throw(Error(32, "Exif metadata", "Photoshop"));
|
| 144 |
144 |
}
|
| 145 |
145 |
|
| 146 |
|
void PsdImage::setIptcData(const IptcData& /*iptcData*/)
|
|
146 |
void PsdImage::setIptcData(const IptcData& /*iptcData*/)
|
| 147 |
147 |
{
|
| 148 |
148 |
// Todo: implement me!
|
| 149 |
149 |
throw(Error(32, "IPTC metadata", "Photoshop"));
|
| 150 |
150 |
}
|
| 151 |
151 |
|
|
152 |
uint32_t PsdImage::_setIptcData(const IptcData& iptcData, BasicIo& out)
|
|
153 |
{
|
|
154 |
uint32_t resLength = 0;
|
|
155 |
byte buf[8];
|
|
156 |
|
|
157 |
if (iptcData.count() > 0) {
|
|
158 |
DataBuf rawIptc = IptcParser::encode(iptcData);
|
|
159 |
if (rawIptc.size_ > 0) {
|
|
160 |
#ifdef DEBUG
|
|
161 |
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_IPTC_NAA << "\n";
|
|
162 |
std::cerr << std::dec << "Writing IPTC_NAA: size: " << rawIptc.size_ << "\n";
|
|
163 |
#endif
|
|
164 |
ul2Data(buf, kPhotoshopResourceType, bigEndian);
|
|
165 |
if (out.write(buf, 4) != 4) throw Error(21);
|
|
166 |
us2Data(buf, kPhotoshopResourceID_IPTC_NAA, bigEndian);
|
|
167 |
if (out.write(buf, 2) != 2) throw Error(21);
|
|
168 |
us2Data(buf, 0, bigEndian); // NULL resource name
|
|
169 |
if (out.write(buf, 2) != 2) throw Error(21);
|
|
170 |
ul2Data(buf, rawIptc.size_, bigEndian);
|
|
171 |
if (out.write(buf, 4) != 4) throw Error(21);
|
|
172 |
// Write encoded Iptc data
|
|
173 |
if (out.write(rawIptc.pData_, rawIptc.size_) != rawIptc.size_) throw Error(21);
|
|
174 |
resLength += rawIptc.size_ + 12;
|
|
175 |
if (rawIptc.size_ & 1) // even padding
|
|
176 |
{
|
|
177 |
buf[0] = 0;
|
|
178 |
if (out.write(buf, 1) != 1) throw Error(21);
|
|
179 |
resLength++;
|
|
180 |
}
|
|
181 |
}
|
|
182 |
}
|
|
183 |
return resLength;
|
|
184 |
}
|
|
185 |
|
|
186 |
uint32_t PsdImage::_setExifData(const ExifData& exifData, BasicIo& out)
|
|
187 |
{
|
|
188 |
uint32_t resLength = 0;
|
|
189 |
byte buf[8];
|
|
190 |
|
|
191 |
if (exifData.count() > 0) {
|
|
192 |
Blob blob;
|
|
193 |
ByteOrder bo = byteOrder();
|
|
194 |
if (bo == invalidByteOrder) {
|
|
195 |
bo = littleEndian;
|
|
196 |
setByteOrder(bo);
|
|
197 |
}
|
|
198 |
ExifParser::encode(blob, bo, exifData);
|
|
199 |
|
|
200 |
if (blob.size() > 0) {
|
|
201 |
#ifdef DEBUG
|
|
202 |
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_ExifInfo << "\n";
|
|
203 |
std::cerr << std::dec << "Writing ExifInfo: size: " << blob.size() << "\n";
|
|
204 |
#endif
|
|
205 |
ul2Data(buf, kPhotoshopResourceType, bigEndian);
|
|
206 |
if (out.write(buf, 4) != 4) throw Error(21);
|
|
207 |
us2Data(buf, kPhotoshopResourceID_ExifInfo, bigEndian);
|
|
208 |
if (out.write(buf, 2) != 2) throw Error(21);
|
|
209 |
us2Data(buf, 0, bigEndian); // NULL resource name
|
|
210 |
if (out.write(buf, 2) != 2) throw Error(21);
|
|
211 |
ul2Data(buf, blob.size(), bigEndian);
|
|
212 |
if (out.write(buf, 4) != 4) throw Error(21);
|
|
213 |
// Write encoded Exif data
|
|
214 |
if (out.write(&blob[0], blob.size()) != blob.size()) throw Error(21);
|
|
215 |
resLength += blob.size() + 12;
|
|
216 |
if (blob.size() & 1) // even padding
|
|
217 |
{
|
|
218 |
buf[0] = 0;
|
|
219 |
if (out.write(buf, 1) != 1) throw Error(21);
|
|
220 |
resLength++;
|
|
221 |
}
|
|
222 |
}
|
|
223 |
}
|
|
224 |
return resLength;
|
|
225 |
}
|
|
226 |
|
|
227 |
uint32_t PsdImage::_setXmpData(const XmpData& xmpData, BasicIo& out)
|
|
228 |
{
|
|
229 |
std::string xmpPacket;
|
|
230 |
uint32_t resLength = 0;
|
|
231 |
byte buf[8];
|
|
232 |
|
|
233 |
#ifdef DEBUG
|
|
234 |
std::cerr << "writeXmpFromPacket(): " << writeXmpFromPacket() << "\n";
|
|
235 |
#endif
|
|
236 |
// writeXmpFromPacket(true);
|
|
237 |
if (writeXmpFromPacket() == false) {
|
|
238 |
if (XmpParser::encode(xmpPacket, xmpData) > 1) {
|
|
239 |
#ifndef SUPPRESS_WARNINGS
|
|
240 |
std::cerr << "Error: Failed to encode XMP metadata.\n";
|
|
241 |
#endif
|
|
242 |
}
|
|
243 |
}
|
|
244 |
|
|
245 |
if (xmpPacket.size() > 0) {
|
|
246 |
#ifdef DEBUG
|
|
247 |
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_XMPPacket << "\n";
|
|
248 |
std::cerr << std::dec << "Writing XMPPacket: size: " << xmpPacket.size() << "\n";
|
|
249 |
#endif
|
|
250 |
ul2Data(buf, kPhotoshopResourceType, bigEndian);
|
|
251 |
if (out.write(buf, 4) != 4) throw Error(21);
|
|
252 |
us2Data(buf, kPhotoshopResourceID_XMPPacket, bigEndian);
|
|
253 |
if (out.write(buf, 2) != 2) throw Error(21);
|
|
254 |
us2Data(buf, 0, bigEndian); // NULL resource name
|
|
255 |
if (out.write(buf, 2) != 2) throw Error(21);
|
|
256 |
ul2Data(buf, xmpPacket.size(), bigEndian);
|
|
257 |
if (out.write(buf, 4) != 4) throw Error(21);
|
|
258 |
// Write XMPPacket
|
|
259 |
if (out.write(reinterpret_cast<const byte*>(xmpPacket.data()), static_cast<long>(xmpPacket.size()))
|
|
260 |
!= static_cast<long>(xmpPacket.size())) throw Error(21);
|
|
261 |
if (out.error()) throw Error(21);
|
|
262 |
resLength += xmpPacket.size() + 12;
|
|
263 |
if (xmpPacket.size() & 1) // even padding
|
|
264 |
{
|
|
265 |
buf[0] = 0;
|
|
266 |
if (out.write(buf, 1) != 1) throw Error(21);
|
|
267 |
resLength++;
|
|
268 |
}
|
|
269 |
}
|
|
270 |
return resLength;
|
|
271 |
}
|
|
272 |
|
|
273 |
|
| 152 |
274 |
void PsdImage::setComment(const std::string& /*comment*/)
|
| 153 |
275 |
{
|
| 154 |
276 |
// Todo: implement me!
|
| ... | ... | |
| 243 |
365 |
uint32_t resourceSize = getULong(buf, bigEndian);
|
| 244 |
366 |
uint32_t curOffset = io_->tell();
|
| 245 |
367 |
|
|
368 |
#ifdef DEBUG
|
|
369 |
std::cerr << std::hex << "resourceId: " << resourceId << std::dec << " length: " << resourceSize << std::hex << "\n";
|
|
370 |
#endif
|
|
371 |
|
| 246 |
372 |
processResourceBlock(resourceId, resourceSize);
|
| 247 |
373 |
resourceSize = (resourceSize + 1) & ~1; // pad to even
|
| 248 |
374 |
io_->seek(curOffset + resourceSize, BasicIo::beg);
|
| ... | ... | |
| 397 |
523 |
// Write oldResLength (will be updated later)
|
| 398 |
524 |
ul2Data(buf, oldResLength, bigEndian);
|
| 399 |
525 |
if (outIo.write(buf, 4) != 4) throw Error(21);
|
|
526 |
|
| 400 |
527 |
#ifdef DEBUG
|
| 401 |
528 |
std::cerr << std::dec << "oldResLength: " << oldResLength << "\n";
|
| 402 |
529 |
#endif
|
| 403 |
|
// Write metadata resource blocks: IPTC_NAA, (TODO: ExifInfo, XMPPacket)
|
| 404 |
|
if (iptcData_.count() > 0) {
|
| 405 |
|
DataBuf rawIptc = IptcParser::encode(iptcData_);
|
| 406 |
|
if (rawIptc.size_ > 0) {
|
| 407 |
|
#ifdef DEBUG
|
| 408 |
|
std::cerr << std::dec << "Writing IPTC_NAA: size: " << rawIptc.size_ << "\n";
|
| 409 |
|
#endif
|
| 410 |
|
ul2Data(buf, kPhotoshopResourceType, bigEndian);
|
| 411 |
|
if (outIo.write(buf, 4) != 4) throw Error(21);
|
| 412 |
|
us2Data(buf, kPhotoshopResourceID_IPTC_NAA, bigEndian);
|
| 413 |
|
if (outIo.write(buf, 2) != 2) throw Error(21);
|
| 414 |
|
us2Data(buf, 0, bigEndian); // NULL resource name
|
| 415 |
|
if (outIo.write(buf, 2) != 2) throw Error(21);
|
| 416 |
|
ul2Data(buf, rawIptc.size_, bigEndian);
|
| 417 |
|
if (outIo.write(buf, 4) != 4) throw Error(21);
|
| 418 |
|
// Write encoded Iptc data
|
| 419 |
|
if (outIo.write(rawIptc.pData_, rawIptc.size_) != rawIptc.size_) throw Error(21);
|
| 420 |
|
newResLength += rawIptc.size_ + 12;
|
| 421 |
|
if (rawIptc.size_ & 1) // even padding
|
| 422 |
|
{
|
| 423 |
|
buf[0] = 0;
|
| 424 |
|
if (outIo.write(buf, 1) != 1) throw Error(21);
|
| 425 |
|
newResLength++;
|
| 426 |
|
}
|
| 427 |
|
}
|
| 428 |
|
}
|
| 429 |
530 |
|
| 430 |
|
// Iterate over original resource blocks and copy those not already processed
|
|
531 |
// Iterate over original resource blocks.
|
|
532 |
// Replace or insert IPTC, EXIF and XMP
|
|
533 |
// Original resource blocks assumed to be sorted ASC
|
|
534 |
|
|
535 |
bool iptcDone = false;
|
|
536 |
bool exifDone = false;
|
|
537 |
bool xmpDone = false;
|
| 431 |
538 |
while (oldResLength > 0)
|
| 432 |
539 |
{
|
| 433 |
540 |
if (io_->read(buf, 8) != 8)
|
| ... | ... | |
| 437 |
544 |
|
| 438 |
545 |
// read resource type and ID
|
| 439 |
546 |
uint32_t resourceType = getULong(buf, bigEndian);
|
| 440 |
|
uint16_t resourceId = getUShort(buf + 4, bigEndian);
|
| 441 |
|
#ifdef DEBUG
|
| 442 |
|
std::cerr << std::hex << "resourceId: " << resourceId << "\n";
|
| 443 |
|
std::cerr << std::dec;
|
| 444 |
|
#endif
|
|
547 |
|
| 445 |
548 |
if (resourceType != kPhotoshopResourceType)
|
| 446 |
549 |
{
|
| 447 |
550 |
break; // bad resource type
|
| 448 |
551 |
}
|
|
552 |
uint16_t resourceId = getUShort(buf + 4, bigEndian);
|
| 449 |
553 |
uint32_t resourceNameLength = buf[6];
|
| 450 |
554 |
uint32_t adjResourceNameLen = resourceNameLength & ~1;
|
| 451 |
555 |
unsigned char resourceNameFirstChar = buf[7];
|
| ... | ... | |
| 465 |
569 |
uint32_t resourceSize = getULong(buf, bigEndian);
|
| 466 |
570 |
uint32_t curOffset = io_->tell();
|
| 467 |
571 |
|
| 468 |
|
switch(resourceId)
|
|
572 |
// Write IPTC_NAA resource block
|
|
573 |
if (resourceId == kPhotoshopResourceID_IPTC_NAA ||
|
|
574 |
(resourceId > kPhotoshopResourceID_IPTC_NAA && iptcDone == false))
|
| 469 |
575 |
{
|
| 470 |
|
case kPhotoshopResourceID_IPTC_NAA:
|
| 471 |
|
{
|
| 472 |
|
resourceSize = (resourceSize + 1) & ~1; // adjust for padding
|
| 473 |
|
break; // already processed
|
| 474 |
|
}
|
| 475 |
|
/*
|
| 476 |
|
case kPhotoshopResourceID_ExifInfo:
|
| 477 |
|
{
|
| 478 |
|
// TODO: skip here, if exiv2 writes it's own EXIF data
|
| 479 |
|
break;
|
| 480 |
|
}
|
|
576 |
newResLength += _setIptcData(iptcData_, outIo);
|
|
577 |
resourceSize = (resourceSize + 1) & ~1; // adjust for padding
|
|
578 |
iptcDone = true;
|
|
579 |
}
|
| 481 |
580 |
|
| 482 |
|
case kPhotoshopResourceID_XMPPacket:
|
| 483 |
|
{
|
| 484 |
|
// TODO: skip here, if exiv2 writes it's own XMP data
|
| 485 |
|
break;
|
| 486 |
|
}
|
| 487 |
|
*/
|
| 488 |
|
default:
|
| 489 |
|
{
|
| 490 |
|
// Copy resource block to new PSD file
|
| 491 |
|
ul2Data(buf, kPhotoshopResourceType, bigEndian);
|
| 492 |
|
if (outIo.write(buf, 4) != 4) throw Error(21);
|
| 493 |
|
us2Data(buf, resourceId, bigEndian);
|
| 494 |
|
if (outIo.write(buf, 2) != 2) throw Error(21);
|
| 495 |
|
// Write resource name as Pascal string
|
| 496 |
|
buf[0] = resourceNameLength & 0x000f;
|
| 497 |
|
if (outIo.write(buf, 1) != 1) throw Error(21);
|
| 498 |
|
buf[0] = resourceNameFirstChar;
|
| 499 |
|
if (outIo.write(buf, 1) != 1) throw Error(21);
|
| 500 |
|
if (outIo.write(resName.pData_, adjResourceNameLen) != adjResourceNameLen) throw Error(21);
|
| 501 |
|
ul2Data(buf, resourceSize, bigEndian);
|
| 502 |
|
if (outIo.write(buf, 4) != 4) throw Error(21);
|
| 503 |
|
|
| 504 |
|
readTotal = 0;
|
| 505 |
|
toRead = 0;
|
| 506 |
|
resourceSize = (resourceSize + 1) & ~1; // pad to even
|
| 507 |
|
while (readTotal < resourceSize) {
|
| 508 |
|
toRead = static_cast<long>(resourceSize - readTotal) < lbuf.size_
|
| 509 |
|
? resourceSize - readTotal : lbuf.size_;
|
| 510 |
|
if (io_->read(lbuf.pData_, toRead) != toRead)
|
| 511 |
|
{
|
| 512 |
|
throw Error(3, "Photoshop");
|
| 513 |
|
}
|
| 514 |
|
readTotal += toRead;
|
| 515 |
|
if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21);
|
|
581 |
// Write ExifInfo resource block
|
|
582 |
else if (resourceId == kPhotoshopResourceID_ExifInfo ||
|
|
583 |
(resourceId > kPhotoshopResourceID_ExifInfo && exifDone == false))
|
|
584 |
{
|
|
585 |
newResLength += _setExifData(exifData_, outIo);
|
|
586 |
resourceSize = (resourceSize + 1) & ~1; // adjust for padding
|
|
587 |
exifDone = true;
|
|
588 |
}
|
|
589 |
|
|
590 |
// Write XMPpacket resource block
|
|
591 |
else if (resourceId == kPhotoshopResourceID_XMPPacket ||
|
|
592 |
(resourceId > kPhotoshopResourceID_XMPPacket && xmpDone == false))
|
|
593 |
{
|
|
594 |
newResLength += _setXmpData(xmpData_, outIo);
|
|
595 |
resourceSize = (resourceSize + 1) & ~1; // adjust for padding
|
|
596 |
xmpDone = true;
|
|
597 |
}
|
|
598 |
|
|
599 |
// Copy all other resource blocks
|
|
600 |
if (resourceId != kPhotoshopResourceID_IPTC_NAA &&
|
|
601 |
resourceId != kPhotoshopResourceID_ExifInfo &&
|
|
602 |
resourceId != kPhotoshopResourceID_XMPPacket)
|
|
603 |
{
|
|
604 |
#ifdef DEBUG
|
|
605 |
std::cerr << std::hex << "copy : resourceId: " << resourceId << "\n";
|
|
606 |
std::cerr << std::dec;
|
|
607 |
#endif
|
|
608 |
// Copy resource block to new PSD file
|
|
609 |
ul2Data(buf, kPhotoshopResourceType, bigEndian);
|
|
610 |
if (outIo.write(buf, 4) != 4) throw Error(21);
|
|
611 |
us2Data(buf, resourceId, bigEndian);
|
|
612 |
if (outIo.write(buf, 2) != 2) throw Error(21);
|
|
613 |
// Write resource name as Pascal string
|
|
614 |
buf[0] = resourceNameLength & 0x000f;
|
|
615 |
if (outIo.write(buf, 1) != 1) throw Error(21);
|
|
616 |
buf[0] = resourceNameFirstChar;
|
|
617 |
if (outIo.write(buf, 1) != 1) throw Error(21);
|
|
618 |
if (outIo.write(resName.pData_, adjResourceNameLen) != adjResourceNameLen) throw Error(21);
|
|
619 |
ul2Data(buf, resourceSize, bigEndian);
|
|
620 |
if (outIo.write(buf, 4) != 4) throw Error(21);
|
|
621 |
|
|
622 |
readTotal = 0;
|
|
623 |
toRead = 0;
|
|
624 |
resourceSize = (resourceSize + 1) & ~1; // pad to even
|
|
625 |
while (readTotal < resourceSize) {
|
|
626 |
toRead = static_cast<long>(resourceSize - readTotal) < lbuf.size_
|
|
627 |
? resourceSize - readTotal : lbuf.size_;
|
|
628 |
if (io_->read(lbuf.pData_, toRead) != toRead)
|
|
629 |
{
|
|
630 |
throw Error(3, "Photoshop");
|
| 516 |
631 |
}
|
| 517 |
|
if (outIo.error()) throw Error(21);
|
| 518 |
|
newResLength += resourceSize + adjResourceNameLen + 12;
|
| 519 |
|
break;
|
|
632 |
readTotal += toRead;
|
|
633 |
if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21);
|
| 520 |
634 |
}
|
|
635 |
if (outIo.error()) throw Error(21);
|
|
636 |
newResLength += resourceSize + adjResourceNameLen + 12;
|
| 521 |
637 |
}
|
| 522 |
638 |
|
| 523 |
639 |
io_->seek(curOffset + resourceSize, BasicIo::beg);
|
| 524 |
640 |
oldResLength -= (12 + adjResourceNameLen + resourceSize);
|
| 525 |
641 |
}
|
| 526 |
642 |
|
|
643 |
// Append IPTC_NAA resource block, if not yet written
|
|
644 |
if (iptcDone == false)
|
|
645 |
{
|
|
646 |
newResLength += _setIptcData(iptcData_, outIo);
|
|
647 |
iptcDone = true;
|
|
648 |
}
|
|
649 |
|
|
650 |
// Append ExifInfo resource block, if not yet written
|
|
651 |
if (exifDone == false)
|
|
652 |
{
|
|
653 |
newResLength += _setExifData(exifData_, outIo);
|
|
654 |
exifDone = true;
|
|
655 |
}
|
|
656 |
|
|
657 |
// Append XmpPacket resource block, if not yet written
|
|
658 |
if (xmpDone == false)
|
|
659 |
{
|
|
660 |
newResLength += _setXmpData(xmpData_, outIo);
|
|
661 |
xmpDone = true;
|
|
662 |
}
|
|
663 |
|
| 527 |
664 |
// Copy remaining data
|
| 528 |
665 |
long readSize = 0;
|
| 529 |
666 |
while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) {
|