diff --git a/app/exiv2.cpp b/app/exiv2.cpp index 18654f3b..84459f60 100644 --- a/app/exiv2.cpp +++ b/app/exiv2.cpp @@ -13,6 +13,7 @@ #include "xmp_exiv2.hpp" #include +#include #include #include #include diff --git a/include/exiv2/exiv2.hpp b/include/exiv2/exiv2.hpp index 66d74d5d..2798f7d5 100644 --- a/include/exiv2/exiv2.hpp +++ b/include/exiv2/exiv2.hpp @@ -29,6 +29,7 @@ #include "exiv2/mrwimage.hpp" #include "exiv2/orfimage.hpp" #include "exiv2/pgfimage.hpp" +#include "exiv2/photoshop.hpp" #ifdef EXV_HAVE_LIBZ #include "exiv2/pngimage.hpp" diff --git a/include/exiv2/jpgimage.hpp b/include/exiv2/jpgimage.hpp index b352df98..128903d7 100644 --- a/include/exiv2/jpgimage.hpp +++ b/include/exiv2/jpgimage.hpp @@ -3,11 +3,8 @@ #ifndef JPGIMAGE_HPP_ #define JPGIMAGE_HPP_ -// ***************************************************************************** #include "exiv2lib_export.h" -#include - // included header files #include "error.hpp" #include "image.hpp" @@ -18,59 +15,6 @@ namespace Exiv2 { // ***************************************************************************** // class definitions -/// @brief Helper class, has methods to deal with %Photoshop "Information Resource Blocks" (IRBs). -struct EXIV2API Photoshop { - // Todo: Public for now - static constexpr std::array irbId_{"8BIM", "AgHg", "DCSR", "PHUT"}; //!< %Photoshop IRB markers - static constexpr auto ps3Id_ = "Photoshop 3.0\0"; //!< %Photoshop marker - static constexpr uint16_t iptc_ = 0x0404; //!< %Photoshop IPTC marker - static constexpr uint16_t preview_ = 0x040c; //!< %Photoshop preview marker - - /// @brief Checks an IRB - /// @param pPsData Existing IRB buffer. It is expected to be of size 4. - /// @return true if the IRB marker is known - /// @todo This should be an implementation detail and not exposed in the API. An attacker could try to pass - /// a smaller buffer or null pointer. - static bool isIrb(const byte* pPsData); - - /// @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;
false otherwise - static bool valid(const byte* pPsData, size_t 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 %Photoshop formated data (from APP13 Jpeg segment) - /// @param sizePsData Size in bytes of pPsData. - /// @param psTag %Tag number of the block to look for. - /// @param record Output value that is set to the start of the data block within pPsData (may not be null). - /// @param sizeHdr Output value that is set to the size of the header within the data block pointed to by record - /// (may not be null). - /// @param sizeData Output value that is set to the size of the actual data within the data block pointed to by record - /// (may not be null). - /// @return 0 if successful;
- /// 3 if no data for psTag was found in pPsData;
- /// -2 if the pPsData buffer does not contain valid data. - static int locateIrb(const byte* pPsData, size_t sizePsData, uint16_t psTag, const byte** record, - uint32_t* const sizeHdr, uint32_t* const sizeData); - - /// @brief Forwards to locateIrb() with \em psTag = \em iptc_ - static int locateIptcIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, - uint32_t* const sizeData); - - /// @brief Forwards to locatePreviewIrb() with \em psTag = \em preview_ - static int locatePreviewIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, - uint32_t* const sizeData); - - /// @brief Set the new IPTC IRB, keeps existing IRBs but removes the IPTC block if there is no new IPTC data to write. - /// @param pPsData Existing IRB buffer - /// @param sizePsData Size of the IRB buffer, may be 0 - /// @param iptcData Iptc data to embed, may be empty - /// @return A data buffer containing the new IRB buffer, may have 0 size - static DataBuf setIptcIrb(const byte* pPsData, size_t sizePsData, const IptcData& iptcData); -}; - /*! @brief Abstract helper base class to access JPEG images. */ diff --git a/include/exiv2/photoshop.hpp b/include/exiv2/photoshop.hpp new file mode 100644 index 00000000..c0c3ae3c --- /dev/null +++ b/include/exiv2/photoshop.hpp @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef PHOTOSHOP_INT_HPP +#define PHOTOSHOP_INT_HPP + +#include "exiv2lib_export.h" + +#include "types.hpp" + +#include + +namespace Exiv2 { +// Forward declarations +class IptcData; + +/// @brief Helper class, has methods to deal with %Photoshop "Information Resource Blocks" (IRBs). +struct EXIV2API Photoshop { + // Todo: Public for now + static constexpr std::array irbId_{"8BIM", "AgHg", "DCSR", "PHUT"}; //!< %Photoshop IRB markers + static constexpr auto ps3Id_ = "Photoshop 3.0\0"; //!< %Photoshop marker + static constexpr uint16_t iptc_ = 0x0404; //!< %Photoshop IPTC marker + static constexpr uint16_t preview_ = 0x040c; //!< %Photoshop preview marker + + /// @brief Checks an IRB + /// @param pPsData Existing IRB buffer. It is expected to be of size 4. + /// @return true if the IRB marker is known + /// @todo This should be an implementation detail and not exposed in the API. An attacker could try to pass + /// a smaller buffer or null pointer. + static bool isIrb(const byte* pPsData); + + /// @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;
false otherwise + static bool valid(const byte* pPsData, size_t 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 %Photoshop formated data (from APP13 Jpeg segment) + /// @param sizePsData Size in bytes of pPsData. + /// @param psTag %Tag number of the block to look for. + /// @param record Output value that is set to the start of the data block within pPsData (may not be null). + /// @param sizeHdr Output value that is set to the size of the header within the data block pointed to by record + /// (may not be null). + /// @param sizeData Output value that is set to the size of the actual data within the data block pointed to by record + /// (may not be null). + /// @return 0 if successful;
+ /// 3 if no data for psTag was found in pPsData;
+ /// -2 if the pPsData buffer does not contain valid data. + static int locateIrb(const byte* pPsData, size_t sizePsData, uint16_t psTag, const byte** record, + uint32_t* const sizeHdr, uint32_t* const sizeData); + + /// @brief Forwards to locateIrb() with \em psTag = \em iptc_ + static int locateIptcIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, + uint32_t* const sizeData); + + /// @brief Forwards to locatePreviewIrb() with \em psTag = \em preview_ + static int locatePreviewIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, + uint32_t* const sizeData); + + /// @brief Set the new IPTC IRB, keeps existing IRBs but removes the IPTC block if there is no new IPTC data to write. + /// @param pPsData Existing IRB buffer + /// @param sizePsData Size of the IRB buffer, may be 0 + /// @param iptcData Iptc data to embed, may be empty + /// @return A data buffer containing the new IRB buffer, may have 0 size + static DataBuf setIptcIrb(const byte* pPsData, size_t sizePsData, const IptcData& iptcData); +}; +} // namespace Exiv2 + +#endif // PHOTOSHOP_INT_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01bfe5df..92126f57 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,6 +65,7 @@ set(PUBLIC_HEADERS ../include/exiv2/mrwimage.hpp ../include/exiv2/orfimage.hpp ../include/exiv2/pgfimage.hpp + ../include/exiv2/photoshop.hpp ../include/exiv2/preview.hpp ../include/exiv2/properties.hpp ../include/exiv2/psdimage.hpp @@ -107,6 +108,7 @@ add_library( exiv2lib mrwimage.cpp orfimage.cpp pgfimage.cpp + photoshop.cpp preview.cpp properties.cpp psdimage.cpp diff --git a/src/image.cpp b/src/image.cpp index 30c75d9c..ad8c2dcc 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -38,6 +38,7 @@ #include "xmpsidecar.hpp" // + standard includes +#include #include #include #include diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index 9f677d6b..023f4c56 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -9,6 +9,7 @@ #include "helper_functions.hpp" #include "image_int.hpp" #include "jpgimage.hpp" +#include "photoshop.hpp" #include "safe_op.hpp" #ifdef WIN32 @@ -21,12 +22,14 @@ #include "fff.h" +#include #include // ***************************************************************************** // class member definitions namespace Exiv2 { + namespace { // JPEG Segment markers (The first byte is always 0xFF, the value of these constants correspond to the 2nd byte) constexpr byte sos_ = 0xda; //!< JPEG SOS marker @@ -79,183 +82,8 @@ void readSegmentSize(const byte marker, BasicIo& io, std::array& buf, s enforce(size >= 2, ErrorCode::kerFailedToReadImageData); } } - } // namespace -bool Photoshop::isIrb(const byte* pPsData) { - if (pPsData == nullptr) { - return false; - } - /// \todo check if direct array comparison is faster than a call to memcmp - return std::any_of(irbId_.begin(), irbId_.end(), [pPsData](auto id) { return memcmp(pPsData, id, 4) == 0; }); -} - -bool Photoshop::valid(const byte* pPsData, size_t sizePsData) { - const byte* record = nullptr; - 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, (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 -int Photoshop::locateIrb(const byte* pPsData, size_t sizePsData, uint16_t psTag, const byte** record, - uint32_t* const sizeHdr, uint32_t* const sizeData) { - if (sizePsData < 12) { - return 3; - } - - // Used for error checking - size_t position = 0; -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "Photoshop::locateIrb: "; -#endif - // Data should follow Photoshop format, if not exit - while (position <= (sizePsData - 12) && isIrb(pPsData + position)) { - const byte* hrd = pPsData + position; - position += 4; - uint16_t type = getUShort(pPsData + position, bigEndian); - position += 2; -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "0x" << std::hex << type << std::dec << " "; -#endif - // Pascal string is padded to have an even size (including size byte) - byte psSize = pPsData[position] + 1; - psSize += (psSize & 1); - position += psSize; - if (position + 4 > sizePsData) { -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "Warning: " - << "Invalid or extended Photoshop IRB\n"; -#endif - return -2; - } - uint32_t dataSize = getULong(pPsData + position, bigEndian); - position += 4; - if (dataSize > (sizePsData - position)) { -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "Warning: " - << "Invalid Photoshop IRB data size " << dataSize << " or extended Photoshop IRB\n"; -#endif - return -2; - } -#ifdef EXIV2_DEBUG_MESSAGES - if ((dataSize & 1) && position + dataSize == sizePsData) { - std::cerr << "Warning: " - << "Photoshop IRB data is not padded to even size\n"; - } -#endif - if (type == psTag) { -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "ok\n"; -#endif - *sizeData = dataSize; - *sizeHdr = psSize + 10; - *record = hrd; - return 0; - } - // Data size is also padded to be even - position += dataSize + (dataSize & 1); - } -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "pPsData doesn't start with '8BIM'\n"; -#endif - if (position < sizePsData) { -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "Warning: " - << "Invalid or extended Photoshop IRB\n"; -#endif - return -2; - } - return 3; -} - -int Photoshop::locateIptcIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, - uint32_t* const sizeData) { - return locateIrb(pPsData, sizePsData, iptc_, record, sizeHdr, sizeData); -} - -int Photoshop::locatePreviewIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, - uint32_t* const sizeData) { - return locateIrb(pPsData, sizePsData, preview_, record, sizeHdr, sizeData); -} - -DataBuf Photoshop::setIptcIrb(const byte* pPsData, size_t sizePsData, const IptcData& iptcData) { -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "IRB block at the beginning of Photoshop::setIptcIrb\n"; - if (sizePsData == 0) - std::cerr << " None.\n"; - else - hexdump(std::cerr, pPsData, sizePsData); -#endif - const byte* record = pPsData; - uint32_t sizeIptc = 0; - uint32_t sizeHdr = 0; - DataBuf rc; - if (0 > Photoshop::locateIptcIrb(pPsData, sizePsData, &record, &sizeHdr, &sizeIptc)) { - return rc; - } - - Blob psBlob; - const auto sizeFront = static_cast(record - pPsData); - // Write data before old record. - if (sizePsData > 0 && sizeFront > 0) { - append(psBlob, pPsData, sizeFront); - } - - // Write new iptc record if we have it - DataBuf rawIptc = IptcParser::encode(iptcData); - if (!rawIptc.empty()) { - std::array tmpBuf; - std::copy_n(Photoshop::irbId_[0], 4, tmpBuf.data()); - us2Data(tmpBuf.data() + 4, iptc_, bigEndian); - tmpBuf[6] = 0; - tmpBuf[7] = 0; - ul2Data(tmpBuf.data() + 8, static_cast(rawIptc.size()), bigEndian); - append(psBlob, tmpBuf.data(), 12); - append(psBlob, rawIptc.c_data(), rawIptc.size()); - // Data is padded to be even (but not included in size) - if (rawIptc.size() & 1) - psBlob.push_back(0x00); - } - - // Write existing stuff after record, skip the current and all remaining IPTC blocks - size_t pos = sizeFront; - auto nextSizeData = Safe::add(static_cast(sizePsData), -static_cast(pos)); - enforce(nextSizeData >= 0, ErrorCode::kerCorruptedMetadata); - while (0 == Photoshop::locateIptcIrb(pPsData + pos, nextSizeData, &record, &sizeHdr, &sizeIptc)) { - const auto newPos = static_cast(record - pPsData); - if (newPos > pos) { // Copy data up to the IPTC IRB - append(psBlob, pPsData + pos, newPos - pos); - } - pos = newPos + sizeHdr + sizeIptc + (sizeIptc & 1); // Skip the IPTC IRB - nextSizeData = Safe::add(static_cast(sizePsData), -static_cast(pos)); - enforce(nextSizeData >= 0, ErrorCode::kerCorruptedMetadata); - } - if (pos < sizePsData) { - append(psBlob, pPsData + pos, sizePsData - pos); - } - - // Data is rounded to be even - if (!psBlob.empty()) - rc = DataBuf(&psBlob[0], psBlob.size()); -#ifdef EXIV2_DEBUG_MESSAGES - std::cerr << "IRB block at the end of Photoshop::setIptcIrb\n"; - if (rc.empty()) - std::cerr << " None.\n"; - else - hexdump(std::cerr, rc.c_data(), rc.size()); -#endif - return rc; -} - JpegBase::JpegBase(ImageType type, BasicIo::UniquePtr io, bool create, const byte initData[], size_t dataSize) : Image(type, mdExif | mdIptc | mdXmp | mdComment, std::move(io)) { if (create) { @@ -282,7 +110,6 @@ byte JpegBase::advanceToMarker(ErrorCode err) const { throw Error(err); } - /// \todo should we check for validity of the marker? 0x59 does not look fine // Markers can start with any number of 0xff while ((c = io_->getb()) == 0xff) { } @@ -316,7 +143,6 @@ void JpegBase::readMetadata() { byte marker = advanceToMarker(ErrorCode::kerNotAJpeg); while (marker != sos_ && marker != eoi_ && search > 0) { - /// @todo the block to read the size of the segment is repeated in 3 places. Factor-out std::array sizebuf; // 2-byte buffer for reading the size. uint16_t size = 0; // Size of the segment, including the 2-byte size field readSegmentSize(marker, *io_, sizebuf, size); diff --git a/src/photoshop.cpp b/src/photoshop.cpp new file mode 100644 index 00000000..cd7f6e52 --- /dev/null +++ b/src/photoshop.cpp @@ -0,0 +1,182 @@ +#include "photoshop.hpp" + +#include "enforce.hpp" +#include "image.hpp" +#include "safe_op.hpp" + +namespace Exiv2 { + +bool Photoshop::isIrb(const byte* data) { + if (data == nullptr) { + return false; + } + return std::any_of(irbId_.begin(), irbId_.end(), [data](auto id) { return memcmp(data, id, 4) == 0; }); +} + +bool Photoshop::valid(const byte* pPsData, size_t sizePsData) { + const byte* record = nullptr; + 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, (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 +int Photoshop::locateIrb(const byte* pPsData, size_t sizePsData, uint16_t psTag, const byte** record, + uint32_t* const sizeHdr, uint32_t* const sizeData) { + if (sizePsData < 12) { + return 3; + } + + // Used for error checking + size_t position = 0; +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "Photoshop::locateIrb: "; +#endif + // Data should follow Photoshop format, if not exit + while (position <= (sizePsData - 12) && isIrb(pPsData + position)) { + const byte* hrd = pPsData + position; + position += 4; + uint16_t type = getUShort(pPsData + position, bigEndian); + position += 2; +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "0x" << std::hex << type << std::dec << " "; +#endif + // Pascal string is padded to have an even size (including size byte) + byte psSize = pPsData[position] + 1; + psSize += (psSize & 1); + position += psSize; + if (position + 4 > sizePsData) { +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "Warning: " + << "Invalid or extended Photoshop IRB\n"; +#endif + return -2; + } + uint32_t dataSize = getULong(pPsData + position, bigEndian); + position += 4; + if (dataSize > (sizePsData - position)) { +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "Warning: " + << "Invalid Photoshop IRB data size " << dataSize << " or extended Photoshop IRB\n"; +#endif + return -2; + } +#ifdef EXIV2_DEBUG_MESSAGES + if ((dataSize & 1) && position + dataSize == sizePsData) { + std::cerr << "Warning: " + << "Photoshop IRB data is not padded to even size\n"; + } +#endif + if (type == psTag) { +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "ok\n"; +#endif + *sizeData = dataSize; + *sizeHdr = psSize + 10; + *record = hrd; + return 0; + } + // Data size is also padded to be even + position += dataSize + (dataSize & 1); + } +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "pPsData doesn't start with '8BIM'\n"; +#endif + if (position < sizePsData) { +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "Warning: " + << "Invalid or extended Photoshop IRB\n"; +#endif + return -2; + } + return 3; +} + +int Photoshop::locateIptcIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, + uint32_t* const sizeData) { + return locateIrb(pPsData, sizePsData, iptc_, record, sizeHdr, sizeData); +} + +int Photoshop::locatePreviewIrb(const byte* pPsData, size_t sizePsData, const byte** record, uint32_t* const sizeHdr, + uint32_t* const sizeData) { + return locateIrb(pPsData, sizePsData, preview_, record, sizeHdr, sizeData); +} + +DataBuf Photoshop::setIptcIrb(const byte* pPsData, size_t sizePsData, const IptcData& iptcData) { +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "IRB block at the beginning of Photoshop::setIptcIrb\n"; + if (sizePsData == 0) + std::cerr << " None.\n"; + else + hexdump(std::cerr, pPsData, sizePsData); +#endif + const byte* record = pPsData; + uint32_t sizeIptc = 0; + uint32_t sizeHdr = 0; + DataBuf rc; + if (0 > Photoshop::locateIptcIrb(pPsData, sizePsData, &record, &sizeHdr, &sizeIptc)) { + return rc; + } + + Blob psBlob; + const auto sizeFront = static_cast(record - pPsData); + // Write data before old record. + if (sizePsData > 0 && sizeFront > 0) { + append(psBlob, pPsData, sizeFront); + } + + // Write new iptc record if we have it + DataBuf rawIptc = IptcParser::encode(iptcData); + if (!rawIptc.empty()) { + std::array tmpBuf; + std::copy_n(Photoshop::irbId_[0], 4, tmpBuf.data()); + us2Data(tmpBuf.data() + 4, iptc_, bigEndian); + tmpBuf[6] = 0; + tmpBuf[7] = 0; + ul2Data(tmpBuf.data() + 8, static_cast(rawIptc.size()), bigEndian); + append(psBlob, tmpBuf.data(), 12); + append(psBlob, rawIptc.c_data(), rawIptc.size()); + // Data is padded to be even (but not included in size) + if (rawIptc.size() & 1) + psBlob.push_back(0x00); + } + + // Write existing stuff after record, skip the current and all remaining IPTC blocks + size_t pos = sizeFront; + long nextSizeData = Safe::add(static_cast(sizePsData), -static_cast(pos)); + enforce(nextSizeData >= 0, ErrorCode::kerCorruptedMetadata); + while (0 == Photoshop::locateIptcIrb(pPsData + pos, nextSizeData, &record, &sizeHdr, &sizeIptc)) { + const auto newPos = static_cast(record - pPsData); + if (newPos > pos) { // Copy data up to the IPTC IRB + append(psBlob, pPsData + pos, newPos - pos); + } + pos = newPos + sizeHdr + sizeIptc + (sizeIptc & 1); // Skip the IPTC IRB + nextSizeData = Safe::add(static_cast(sizePsData), -static_cast(pos)); + enforce(nextSizeData >= 0, ErrorCode::kerCorruptedMetadata); + } + if (pos < sizePsData) { + append(psBlob, pPsData + pos, sizePsData - pos); + } + + // Data is rounded to be even + if (!psBlob.empty()) + rc = DataBuf(&psBlob[0], psBlob.size()); +#ifdef EXIV2_DEBUG_MESSAGES + std::cerr << "IRB block at the end of Photoshop::setIptcIrb\n"; + if (rc.empty()) + std::cerr << " None.\n"; + else + hexdump(std::cerr, rc.c_data(), rc.size()); +#endif + return rc; +} + +} // namespace Exiv2 diff --git a/src/pngchunk_int.cpp b/src/pngchunk_int.cpp index f41f9bf1..4e18431b 100644 --- a/src/pngchunk_int.cpp +++ b/src/pngchunk_int.cpp @@ -13,12 +13,14 @@ #include "image.hpp" #include "iptc.hpp" #include "jpgimage.hpp" +#include "photoshop.hpp" #include "pngchunk_int.hpp" #include "safe_op.hpp" #include "tiffimage.hpp" // standard includes #include +#include #include #include #include diff --git a/src/pngimage.cpp b/src/pngimage.cpp index 0743ab9c..d1c6a363 100644 --- a/src/pngimage.cpp +++ b/src/pngimage.cpp @@ -13,11 +13,13 @@ #include "image.hpp" #include "image_int.hpp" #include "jpgimage.hpp" +#include "photoshop.hpp" #include "pngchunk_int.hpp" #include "pngimage.hpp" #include "tiffimage.hpp" #include "types.hpp" +#include #include namespace { diff --git a/src/preview.cpp b/src/preview.cpp index 82d9899c..ee7e6415 100644 --- a/src/preview.cpp +++ b/src/preview.cpp @@ -8,6 +8,7 @@ #include "futils.hpp" #include "image.hpp" #include "jpgimage.hpp" +#include "photoshop.hpp" #include "safe_op.hpp" #include "tiffimage.hpp" #include "tiffimage_int.hpp" diff --git a/src/psdimage.cpp b/src/psdimage.cpp index 4df8f882..a1e91151 100644 --- a/src/psdimage.cpp +++ b/src/psdimage.cpp @@ -10,6 +10,7 @@ #include "futils.hpp" #include "image.hpp" #include "jpgimage.hpp" +#include "photoshop.hpp" #include diff --git a/src/tiffvisitor_int.cpp b/src/tiffvisitor_int.cpp index ac41f133..190b348e 100644 --- a/src/tiffvisitor_int.cpp +++ b/src/tiffvisitor_int.cpp @@ -8,6 +8,7 @@ #include "iptc.hpp" #include "jpgimage.hpp" #include "makernote_int.hpp" +#include "photoshop.hpp" #include "sonymn_int.hpp" #include "tiffcomposite_int.hpp" // Do not change the order of these 2 includes, #include "tiffimage_int.hpp" diff --git a/unitTests/test_Photoshop.cpp b/unitTests/test_Photoshop.cpp index 98b69e33..1dd31b98 100644 --- a/unitTests/test_Photoshop.cpp +++ b/unitTests/test_Photoshop.cpp @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include "photoshop.hpp" + +#include "error.hpp" +#include "iptc.hpp" #include