Project

General

Profile

patch: write EXIF/XMP to psd » psdimage.cpp.patch

Michael Ulbrich, 28 Feb 2009 12:34

View differences:

psdimage.cpp 2009-02-24 11:22:48.000000000 +0100
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_))) {
(1-1/2)