view src/exiv2-improvements.patch @ 616:3b62d2016275

new package: exiv2
author Volker Grabsch <vog@notjusthosting.com>
date Mon, 21 Dec 2009 18:16:39 +0100
parents
children
line wrap: on
line source

Copyright (C) 2009  Volker Grabsch

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject
to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Fri Jul  3 11:44:14 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * avoid_extended_Photoshop_IRB_warnings
Fri Jun 19 17:57:06 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * handle_extended_Photoshop_IRBs
Thu Jun  4 20:12:36 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * recognize_small_corrupt_IRBs
Thu Jun  4 15:48:36 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * new_function_Photoshop_valid
Thu Jun  4 13:05:49 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * skip_writing_redundant_IPTC_IRBs
Tue May 19 16:03:52 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * read_and_modify_only_the_first_XMP_segment
Tue May 19 14:05:38 CEST 2009  Volker Grabsch <vog@notjusthosting.com>
  * handle_empty_IRB
diff -rN -u old-trunk-1/src/jpgimage.cpp new-trunk-1/src/jpgimage.cpp
--- old-trunk-1/src/jpgimage.cpp	2009-06-25 17:10:32.000000000 +0200
+++ new-trunk-1/src/jpgimage.cpp	2009-06-25 17:10:32.000000000 +0200
@@ -87,6 +87,23 @@
     const char     Photoshop::bimId_[] = "8BIM";
     const uint16_t Photoshop::iptc_    = 0x0404;
 
+    bool Photoshop::valid(const byte* pPsData,
+                          long        sizePsData)
+    {
+        const byte *record = 0;
+        uint32_t sizeIptc = 0;
+        uint32_t sizeHdr = 0;
+        const byte* pCur = pPsData;
+        const byte* pEnd = pPsData + sizePsData;
+        int ret = 0;
+        while (pCur < pEnd
+               && 0 == (ret = Photoshop::locateIptcIrb(pCur, static_cast<long>(pEnd - pCur),
+                                                       &record, &sizeHdr, &sizeIptc))) {
+            pCur = record + sizeHdr + sizeIptc + (sizeIptc & 1);
+        }
+        return ret >= 0;
+    }
+
     // Todo: Generalised from JpegBase::locateIptcData without really understanding
     //       the format (in particular the header). So it remains to be confirmed
     //       if this also makes sense for psTag != Photoshop::iptc
@@ -106,7 +123,7 @@
         std::cerr << "Photoshop::locateIrb: ";
 #endif
         // Data should follow Photoshop format, if not exit
-        while (   position <= sizePsData - 14
+        while (   position <= sizePsData - 12
                && memcmp(pPsData + position, Photoshop::bimId_, 4) == 0) {
             const byte *hrd = pPsData + position;
             position += 4;
@@ -121,8 +138,8 @@
             position += psSize;
             if (position + 4 > sizePsData) {
 #ifndef SUPPRESS_WARNINGS
-                std::cerr << "Error: "
-                          << "Invalid Photoshop IRB\n";
+                std::cerr << "Warning: "
+                          << "Invalid or extended Photoshop IRB\n";
 #endif
                 return -2;
             }
@@ -130,9 +147,9 @@
             position += 4;
             if (dataSize > static_cast<uint32_t>(sizePsData - position)) {
 #ifndef SUPPRESS_WARNINGS
-                std::cerr << "Error: "
+                std::cerr << "Warning: "
                           << "Invalid Photoshop IRB data size "
-                          << dataSize << "\n";
+                          << dataSize << " or extended Photoshop IRB\n";
 #endif
                 return -2;
             }
@@ -158,6 +175,13 @@
 #ifdef DEBUG
         std::cerr << "pPsData doesn't start with '8BIM'\n";
 #endif
+        if (position < sizePsData) {
+#ifndef SUPPRESS_WARNINGS
+            std::cerr << "Warning: "
+                      << "Invalid or extended Photoshop IRB\n";
+#endif
+            return -2;
+        }
         return 3;
     } // Photoshop::locateIrb
 
@@ -210,15 +234,23 @@
             // Data is padded to be even (but not included in size)
             if (rawIptc.size_ & 1) psBlob.push_back(0x00);
         }
-        // Write existing stuff after record, data is rounded to be even.
-        const uint32_t sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1);
-        // Note: Because of the rounding, sizeFront + sizeOldData can be
-        // _greater_ than sizePsData by 1 (not just equal), if the original
-        // data was not padded.
-        if (static_cast<uint32_t>(sizePsData) > sizeFront + sizeOldData) {
-            append(psBlob, record + sizeOldData,
-                   sizePsData - sizeFront - sizeOldData);
+        // Write existing stuff after record,
+        // skip the current and all remaining IPTC blocks
+        long pos = sizeFront;
+        while (0 == Photoshop::locateIptcIrb(pPsData + pos, sizePsData - pos,
+                                             &record, &sizeHdr, &sizeIptc)) {
+            const long newPos = static_cast<long>(record - pPsData);
+            // Copy data up to the IPTC IRB
+            if (newPos > pos) {
+                append(psBlob, pPsData + pos, newPos - pos);
+            }
+            // Skip the IPTC IRB
+            pos = newPos + sizeHdr + sizeIptc + (sizeIptc & 1);
+        }
+        if (pos < sizePsData) {
+            append(psBlob, pPsData + pos, sizePsData - pos);
         }
+        // Data is rounded to be even
         if (psBlob.size() > 0) rc = DataBuf(&psBlob[0], static_cast<long>(psBlob.size()));
 #ifdef DEBUG
         std::cerr << "IRB block at the end of Photoshop::setIptcIrb\n";
@@ -281,9 +313,10 @@
         const long bufMinSize = 36;
         long bufRead = 0;
         DataBuf buf(bufMinSize);
-        Blob iptcBlob;
-        bool foundPsData = false;
+        Blob psBlob;
+        bool foundCompletePsData = false;
         bool foundExifData = false;
+        bool foundXmpData = false;
 
         // Read section marker
         int marker = advanceToMarker();
@@ -297,15 +330,6 @@
             if (bufRead < 2) throw Error(15);
             uint16_t size = getUShort(buf.pData_, bigEndian);
 
-            if (foundPsData && marker != app13_) {
-                // For IPTC, decrement search only after all app13 segments are
-                // loaded, assuming they all appear in sequence. But decode IPTC
-                // data after the loop, in case an app13 is the last segment
-                // before sos or eoi.
-                foundPsData = false;
-                if (--search == 0) break;
-            }
-
             if (   !foundExifData
                 && marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
                 if (size < 8) {
@@ -328,7 +352,8 @@
                 --search;
                 foundExifData = true;
             }
-            else if (marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
+            else if (   !foundXmpData
+                     && marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
                 if (size < 31) {
                     rc = 6;
                     break;
@@ -345,9 +370,10 @@
 #endif
                 }
                 --search;
+                foundXmpData = true;
             }
-            else if (   marker == app13_
-                     && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
+            else if (   !foundCompletePsData
+                     && marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
                 if (size < 16) {
                     rc = 2;
                     break;
@@ -357,32 +383,17 @@
                 DataBuf psData(size - 16);
                 io_->read(psData.pData_, psData.size_);
                 if (io_->error() || io_->eof()) throw Error(14);
-                const byte *record = 0;
-                uint32_t sizeIptc = 0;
-                uint32_t sizeHdr = 0;
 #ifdef DEBUG
                 std::cerr << "Found app13 segment, size = " << size << "\n";
                 //hexdump(std::cerr, psData.pData_, psData.size_);
 #endif
-                // Find actual IPTC data within the APP13 segment
-                const byte* pEnd = psData.pData_ + psData.size_;
-                const byte* pCur = psData.pData_;
-                while (   pCur < pEnd
-                       && 0 == Photoshop::locateIptcIrb(pCur,
-                                                        static_cast<long>(pEnd - pCur),
-                                                        &record,
-                                                        &sizeHdr,
-                                                        &sizeIptc)) {
-                    if (sizeIptc) {
-#ifdef DEBUG
-                        std::cerr << "Found IPTC IRB, size = " << sizeIptc << "\n";
-#endif
-                        append(iptcBlob, record + sizeHdr, sizeIptc);
-                    }
-                    pCur = record + sizeHdr + sizeIptc;
-                    pCur += (sizeIptc & 1);
+                // Append to psBlob
+                append(psBlob, psData.pData_, psData.size_);
+                // Check whether psBlob is complete
+                if (Photoshop::valid(&psBlob[0], psBlob.size())) {
+                    --search;
+                    foundCompletePsData = true;
                 }
-                foundPsData = true;
             }
             else if (marker == com_ && comment_.empty())
             {
@@ -437,6 +448,24 @@
             }
         } // while there are segments to process
 
+        // Find actual IPTC data within the psBlob
+        Blob iptcBlob;
+        const byte *record = 0;
+        uint32_t sizeIptc = 0;
+        uint32_t sizeHdr = 0;
+        const byte* pCur = &psBlob[0];
+        const byte* pEnd = pCur + psBlob.size();
+        while (   pCur < pEnd
+               && 0 == Photoshop::locateIptcIrb(pCur, static_cast<long>(pEnd - pCur),
+                                                &record, &sizeHdr, &sizeIptc)) {
+#ifdef DEBUG
+            std::cerr << "Found IPTC IRB, size = " << sizeIptc << "\n";
+#endif
+            if (sizeIptc) {
+                append(iptcBlob, record + sizeHdr, sizeIptc);
+            }
+            pCur = record + sizeHdr + sizeIptc + (sizeIptc & 1);
+        }
         if (   iptcBlob.size() > 0
             && IptcParser::decode(iptcData_,
                                   &iptcBlob[0],
@@ -489,9 +518,10 @@
         int comPos = 0;
         int skipApp1Exif = -1;
         int skipApp1Xmp = -1;
-        int skipApp13Ps3 = -1;
+        bool foundCompletePsData = false;
+        std::vector<int> skipApp13Ps3;
         int skipCom = -1;
-        DataBuf psData;
+        Blob psBlob;
         DataBuf rawExif;
 
         // Write image header
@@ -526,24 +556,31 @@
                 io_->read(rawExif.pData_, rawExif.size_);
                 if (io_->error() || io_->eof()) throw Error(22);
             }
-            else if (marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
+            else if (   skipApp1Xmp == -1
+                     && marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
                 if (size < 31) throw Error(22);
                 skipApp1Xmp = count;
                 ++search;
                 if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
             }
-            else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
+            else if (   !foundCompletePsData
+                     && marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
 #ifdef DEBUG
                 std::cerr << "Found APP13 Photoshop PS3 segment\n";
 #endif
                 if (size < 16) throw Error(22);
-                skipApp13Ps3 = count;
-                ++search;
+                skipApp13Ps3.push_back(count);
                 io_->seek(16 - bufRead, BasicIo::cur);
-                psData.alloc(size - 16);
                 // Load PS data now to allow reinsertion at any point
+                DataBuf psData(size - 16);
                 io_->read(psData.pData_, size - 16);
                 if (io_->error() || io_->eof()) throw Error(20);
+                // Append to psBlob
+                append(psBlob, psData.pData_, psData.size_);
+                // Check whether psBlob is complete
+                if (Photoshop::valid(&psBlob[0], psBlob.size())) {
+                    foundCompletePsData = true;
+                }
             }
             else if (marker == com_ && skipCom == -1) {
                 if (size < 2) throw Error(22);
@@ -583,6 +620,10 @@
             if (marker < 0) throw Error(22);
             ++count;
         }
+
+        if (!foundCompletePsData && skipApp13Ps3.size() > 0) throw Error(22);
+        search += skipApp13Ps3.size();
+
         if (comPos == 0) {
             if (marker == eoi_) comPos = count;
             else comPos = insertPos;
@@ -591,7 +632,7 @@
         if (exifData_.count() > 0) ++search;
         if (writeXmpFromPacket() == false && xmpData_.count() > 0) ++search;
         if (writeXmpFromPacket() == true && xmpPacket_.size() > 0) ++search;
-        if (iptcData_.count() > 0) ++search;
+        if (foundCompletePsData || iptcData_.count() > 0) ++search;
         if (!comment_.empty()) ++search;
 
         io_->seek(seek, BasicIo::beg);
@@ -673,31 +714,44 @@
                     if (outIo.error()) throw Error(21);
                     --search;
                 }
-                if (psData.size_ > 0 || iptcData_.count() > 0) {
+                if (foundCompletePsData || iptcData_.count() > 0) {
                     // Set the new IPTC IRB, keeps existing IRBs but removes the
                     // IPTC block if there is no new IPTC data to write
-                    DataBuf newPsData = Photoshop::setIptcIrb(psData.pData_,
-                                                              psData.size_,
+                    DataBuf newPsData = Photoshop::setIptcIrb(&psBlob[0],
+                                                              psBlob.size(),
                                                               iptcData_);
-                    if (newPsData.size_ > 0) {
-                        // Write APP13 marker, new size, and ps3Id
+                    const long maxChunkSize = 0xffff - 16;
+                    const byte* chunkStart = newPsData.pData_;
+                    const byte* chunkEnd = chunkStart + newPsData.size_;
+                    while (chunkStart < chunkEnd) {
+                        // Determine size of next chunk
+                        long chunkSize = static_cast<long>(chunkEnd - chunkStart);
+                        if (chunkSize > maxChunkSize) {
+                            chunkSize = maxChunkSize;
+                            // Don't break at a valid IRB boundary
+                            const long writtenSize = static_cast<long>(chunkStart - newPsData.pData_);
+                            if (Photoshop::valid(newPsData.pData_, writtenSize + chunkSize)) {
+                                // Since an IRB has minimum size 12,
+                                // (chunkSize - 8) can't be also a IRB boundary
+                                chunkSize -= 8;
+                            }
+                        }
+
+                        // Write APP13 marker, chunk size, and ps3Id
                         tmpBuf[0] = 0xff;
                         tmpBuf[1] = app13_;
-
-                        if (newPsData.size_ + 16 > 0xffff) throw Error(37, "IPTC");
-                        us2Data(tmpBuf + 2, static_cast<uint16_t>(newPsData.size_ + 16), bigEndian);
+                        us2Data(tmpBuf + 2, static_cast<uint16_t>(chunkSize + 16), bigEndian);
                         std::memcpy(tmpBuf + 4, Photoshop::ps3Id_, 14);
                         if (outIo.write(tmpBuf, 18) != 18) throw Error(21);
                         if (outIo.error()) throw Error(21);
 
-                        // Write new Photoshop IRB data buffer
-                        if (   outIo.write(newPsData.pData_, newPsData.size_)
-                            != newPsData.size_) throw Error(21);
+                        // Write next chunk of the Photoshop IRB data buffer
+                        if (outIo.write(chunkStart, chunkSize) != chunkSize) throw Error(21);
                         if (outIo.error()) throw Error(21);
+
+                        chunkStart += chunkSize;
                     }
-                    if (iptcData_.count() > 0) {
-                        --search;
-                    }
+                    --search;
                 }
             }
             if (comPos == count) {
@@ -724,7 +778,7 @@
             }
             else if (   skipApp1Exif == count
                      || skipApp1Xmp  == count
-                     || skipApp13Ps3 == count
+                     || find(skipApp13Ps3.begin(), skipApp13Ps3.end(), count) != skipApp13Ps3.end()
                      || skipCom      == count) {
                 --search;
                 io_->seek(size-bufRead, BasicIo::cur);
diff -rN -u old-trunk-1/src/jpgimage.hpp new-trunk-1/src/jpgimage.hpp
--- old-trunk-1/src/jpgimage.hpp	2009-06-25 17:10:32.000000000 +0200
+++ new-trunk-1/src/jpgimage.hpp	2009-06-25 17:10:32.000000000 +0200
@@ -64,6 +64,16 @@
         static const uint16_t iptc_;    //!< %Photoshop IPTC marker
 
         /*!
+          @brief Validates all IRBs
+
+          @param pPsData        Existing IRB buffer
+          @param sizePsData     Size of the IRB buffer, may be 0
+          @return true  if all IRBs are valid;<BR>
+                  false otherwise
+        */
+        static bool valid(const byte* pPsData,
+                          long        sizePsData);
+        /*!
           @brief Locates the data for a %Photoshop tag in a %Photoshop formated memory
               buffer. Operates on raw data to simplify reuse.
           @param pPsData Pointer to buffer containing entire payload of

diff -rN -u old-trunk-1/src/jpgimage.cpp new-trunk-1/src/jpgimage.cpp
--- old-trunk-1/src/jpgimage.cpp	2009-07-03 12:02:48.000000000 +0200
+++ new-trunk-1/src/jpgimage.cpp	2009-07-03 12:02:48.000000000 +0200
@@ -137,7 +137,7 @@
             psSize += (psSize & 1);
             position += psSize;
             if (position + 4 > sizePsData) {
-#ifndef SUPPRESS_WARNINGS
+#ifdef DEBUG
                 std::cerr << "Warning: "
                           << "Invalid or extended Photoshop IRB\n";
 #endif
@@ -146,7 +146,7 @@
             uint32_t dataSize = getULong(pPsData + position, bigEndian);
             position += 4;
             if (dataSize > static_cast<uint32_t>(sizePsData - position)) {
-#ifndef SUPPRESS_WARNINGS
+#ifdef DEBUG
                 std::cerr << "Warning: "
                           << "Invalid Photoshop IRB data size "
                           << dataSize << " or extended Photoshop IRB\n";
@@ -176,7 +176,7 @@
         std::cerr << "pPsData doesn't start with '8BIM'\n";
 #endif
         if (position < sizePsData) {
-#ifndef SUPPRESS_WARNINGS
+#ifdef DEBUG
             std::cerr << "Warning: "
                       << "Invalid or extended Photoshop IRB\n";
 #endif