From 2e9138cf9107fb0ae3834797ea74b5934b24b2f0 Mon Sep 17 00:00:00 2001 From: Robin Mills Date: Thu, 13 Oct 2016 11:03:39 +0000 Subject: [PATCH] #1243 Work-in-progress. Still to add code to writeMetadata for icc profiles. --- src/image.cpp | 1 + src/jp2image.cpp | 114 +++++++++++++++++++++--------------------- test/data/Reagan2.jp2 | Bin 0 -> 8389 bytes 3 files changed, 59 insertions(+), 56 deletions(-) create mode 100644 test/data/Reagan2.jp2 diff --git a/src/image.cpp b/src/image.cpp index 68e32789..58c06be8 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -651,6 +651,7 @@ namespace Exiv2 { std::string binaryToString(DataBuf& buf, size_t size, size_t start /*=0*/) { + if ( size > buf.size_ ) size = buf.size_; return binaryToString(buf.pData_,size,start); } diff --git a/src/jp2image.cpp b/src/jp2image.cpp index 4bd5acc0..d08d69a8 100644 --- a/src/jp2image.cpp +++ b/src/jp2image.cpp @@ -26,6 +26,8 @@ #include "rcsid_int.hpp" EXIV2_RCSID("@(#) $Id$") +// #define DEBUG + // included header files #include "config.h" @@ -50,6 +52,7 @@ const uint32_t kJp2BoxTypeJp2Header = 0x6a703268; // 'jp2h' const uint32_t kJp2BoxTypeImageHeader = 0x69686472; // 'ihdr' const uint32_t kJp2BoxTypeColorHeader = 0x636f6c72; // 'colr' const uint32_t kJp2BoxTypeUuid = 0x75756964; // 'uuid' +const uint32_t kJp2BoxTypeClose = 0x6a703263; // 'jp2c' // from openjpeg-2.1.2/src/lib/openjp2/jp2.h /*#define JPIP_JPIP 0x6a706970*/ @@ -143,13 +146,13 @@ namespace Exiv2 if (io_->open() == 0) { #ifdef DEBUG - std::cerr << "Exiv2::Jp2Image:: Creating JPEG2000 image to memory\n"; + std::cerr << "Exiv2::Jp2Image:: Creating JPEG2000 image to memory" << std::endl; #endif IoCloser closer(*io_); if (io_->write(Jp2Blank, sizeof(Jp2Blank)) != sizeof(Jp2Blank)) { #ifdef DEBUG - std::cerr << "Exiv2::Jp2Image:: Failed to create JPEG2000 image on memory\n"; + std::cerr << "Exiv2::Jp2Image:: Failed to create JPEG2000 image on memory" << std::endl; #endif } } @@ -178,7 +181,7 @@ namespace Exiv2 void Jp2Image::readMetadata() { #ifdef DEBUG - std::cerr << "Exiv2::Jp2Image::readMetadata: Reading JPEG-2000 file " << io_->path() << "\n"; + std::cerr << "Exiv2::Jp2Image::readMetadata: Reading JPEG-2000 file " << io_->path() << std::endl; #endif if (io_->open() != 0) { @@ -204,19 +207,15 @@ namespace Exiv2 box.length = getLong((byte*)&box.length, bigEndian); box.type = getLong((byte*)&box.type, bigEndian); #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Position: " << position << "\n"; - std::cout << "Exiv2::Jp2Image::readMetadata: Find box type: " << toAscii(box.type) - << " length: " << box.length << "\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: " + << "Position: " << position + << " box type: " << toAscii(box.type) + << " length: " << box.length + << std::endl; #endif - if (box.length == 0) - { -#ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Null Box size has been found. " - "This is the last box of file.\n"; -#endif - return; - } + if (box.length == 0) return ; + if (box.length == 1) { // FIXME. Special case. the real box size is given in another place. @@ -227,7 +226,7 @@ namespace Exiv2 case kJp2BoxTypeJp2Header: { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: JP2Header box found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: JP2Header box found" << std::endl; #endif long restore = io_->tell(); @@ -236,12 +235,14 @@ namespace Exiv2 subBox.length = getLong((byte*)&subBox.length, bigEndian); subBox.type = getLong((byte*)&subBox.type, bigEndian); #ifdef DEBUG - std::cout << "subBox = " << toAscii(subBox.type) << " length = " << subBox.length << std::endl; + std::cout << "Exiv2::Jp2Image::readMetadata: " + << "subBox = " << toAscii(subBox.type) << " length = " << subBox.length << std::endl; #endif if(subBox.type == kJp2BoxTypeColorHeader) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Color data found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: " + << "Color data found" << std::endl; #endif long pad = 3 ; // don't know why there are 3 padding bytes DataBuf data(subBox.length+8); @@ -256,8 +257,7 @@ namespace Exiv2 fwrite(icc.pData_,icc.size_,1,f); fclose(f); } - std::cout << "Exiv2::Jp2Image::readMetadata: wrote iccProfile to " << iccPath << std::endl ; - + std::cout << "Exiv2::Jp2Image::readMetadata: wrote iccProfile " << icc.size_<< " bytes to " << iccPath << std::endl ; #endif setIccProfile(icc); } @@ -266,9 +266,8 @@ namespace Exiv2 { io_->read((byte*)&ihdr, sizeof(ihdr)); #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Ihdr data found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: Ihdr data found" << std::endl; #endif - ihdr.imageHeight = getLong((byte*)&ihdr.imageHeight, bigEndian); ihdr.imageWidth = getLong((byte*)&ihdr.imageWidth, bigEndian); ihdr.componentCount = getShort((byte*)&ihdr.componentCount, bigEndian); @@ -284,10 +283,11 @@ namespace Exiv2 } break; } + case kJp2BoxTypeUuid: { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: UUID box found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: UUID box found" << std::endl; #endif if (io_->read((byte*)&uuid, sizeof(uuid)) == sizeof(uuid)) @@ -301,7 +301,7 @@ namespace Exiv2 if(bIsExif) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Exif data found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: Exif data found" << std::endl ; #endif rawData.alloc(box.length - (sizeof(box) + sizeof(uuid))); bufRead = io_->read(rawData.pData_, rawData.size_); @@ -342,7 +342,7 @@ namespace Exiv2 else { #ifndef SUPPRESS_WARNINGS - EXV_WARNING << "Failed to decode Exif metadata.\n"; + EXV_WARNING << "Failed to decode Exif metadata." << std::endl; #endif exifData_.clear(); } @@ -351,7 +351,7 @@ namespace Exiv2 if(bIsIPTC) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Iptc data found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: Iptc data found" << std::endl; #endif rawData.alloc(box.length - (sizeof(box) + sizeof(uuid))); bufRead = io_->read(rawData.pData_, rawData.size_); @@ -361,7 +361,7 @@ namespace Exiv2 if (IptcParser::decode(iptcData_, rawData.pData_, rawData.size_)) { #ifndef SUPPRESS_WARNINGS - EXV_WARNING << "Failed to decode IPTC metadata.\n"; + EXV_WARNING << "Failed to decode IPTC metadata." << std::endl; #endif iptcData_.clear(); } @@ -370,7 +370,7 @@ namespace Exiv2 if(bIsXMP) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::readMetadata: Xmp data found\n"; + std::cout << "Exiv2::Jp2Image::readMetadata: Xmp data found" << std::endl; #endif rawData.alloc(box.length - (uint32_t)(sizeof(box) + sizeof(uuid))); bufRead = io_->read(rawData.pData_, rawData.size_); @@ -383,7 +383,7 @@ namespace Exiv2 { #ifndef SUPPRESS_WARNINGS EXV_WARNING << "Removing " << static_cast(idx) - << " characters from the beginning of the XMP packet\n"; + << " characters from the beginning of the XMP packet" << std::endl; #endif xmpPacket_ = xmpPacket_.substr(idx); } @@ -391,7 +391,7 @@ namespace Exiv2 if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) { #ifndef SUPPRESS_WARNINGS - EXV_WARNING << "Failed to decode XMP metadata.\n"; + EXV_WARNING << "Failed to decode XMP metadata." << std::endl; #endif } } @@ -407,7 +407,7 @@ namespace Exiv2 // Move to the next box. io_->seek(static_cast(position - sizeof(box) + box.length), BasicIo::beg); - if (io_->error()/* || io_->eof()*/) throw Error(14); + if (io_->error()) throw Error(14); } } // Jp2Image::readMetadata @@ -475,22 +475,24 @@ namespace Exiv2 bLF = true ; } - if (box.length) switch(box.type) + if (box.length && box.type != kJp2BoxTypeClose) switch(box.type) { case kJp2BoxTypeJp2Header: { long restore = io_->tell(); lf(out,bLF); - while (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox) && subBox.length ) + while (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox)) { subBox.length = getLong((byte*)&subBox.length, bigEndian); subBox.type = getLong((byte*)&subBox.type, bigEndian); - DataBuf data(subBox.length+8); + if (subBox.type == kJp2BoxTypeClose || subBox.length < sizeof(box)) break; + + DataBuf data(subBox.length-sizeof(box)); io_->read(data.pData_,data.size_); if ( bPrint ) out << Internal::stringFormat("%8ld | %8ld | sub:",restore,subBox.length) << toAscii(subBox.type) - <<" | " << Internal::binaryToString(data.pData_,data.size_< 40 ? data.size_:40,sizeof(uuid)) << std::endl; + <<" | " << Internal::binaryToString(data,40,0) << std::endl; if(subBox.type == kJp2BoxTypeColorHeader) { @@ -525,19 +527,19 @@ namespace Exiv2 } DataBuf rawData; - rawData.alloc(box.length-sizeof(uuid)-sizeof(box)); + rawData.alloc(box.length-sizeof(uuid)-sizeof(box)); long bufRead = io_->read(rawData.pData_, rawData.size_); if (io_->error()) throw Error(14); if (bufRead != rawData.size_) throw Error(20); - if ( bPrint )out << Internal::binaryToString(rawData.pData_,rawData.size_< 40 ? rawData.size_:40,0); + if ( bPrint )out << Internal::binaryToString(rawData,40,0); lf(out,bLF); if(bIsExif && bRecursive && rawData.size_ > 0) { if ( (rawData.pData_[0] == rawData.pData_[1]) - && (rawData.pData_[0]=='I' || rawData.pData_[0]=='M' ) - ) { + && (rawData.pData_[0]=='I' || rawData.pData_[0]=='M' ) + ) { BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(rawData.pData_,rawData.size_)); TiffImage::printTiffStructure(*p,out,option,depth); } @@ -593,8 +595,8 @@ namespace Exiv2 if (!outIo.isopen()) throw Error(21); #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << "\n"; - std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << "\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << std::endl; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << std::endl; #endif // Ensure that this is the correct image type @@ -619,7 +621,7 @@ namespace Exiv2 while(io_->tell() < io_->size()) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << "\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << std::endl; #endif // Read chunk header. @@ -636,14 +638,14 @@ namespace Exiv2 #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Find box type: " << bheaderBuf.pData_ + 4 - << " length: " << box.length << "\n"; + << " length: " << box.length << std::endl; #endif if (box.length == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Null Box size has been found. " - "This is the last box of file.\n"; + "This is the last box of file." << std::endl; #endif box.length = io_->size() - io_->tell() + 8; } @@ -660,7 +662,7 @@ namespace Exiv2 if (io_->error()) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: Error reading source file\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: Error reading source file" << std::endl; #endif throw Error(14); @@ -669,7 +671,7 @@ namespace Exiv2 if (bufRead != (long)(box.length - 8)) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: Cannot read source file data\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: Cannot read source file data" << std::endl; #endif throw Error(20); } @@ -680,7 +682,7 @@ namespace Exiv2 { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (length: " << box.length << ")\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (length: " << box.length << ")" << std::endl; #endif if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21); @@ -707,7 +709,7 @@ namespace Exiv2 #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Exif metadata (length: " - << boxData.size_ << ")\n"; + << boxData.size_ << std::endl; #endif if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21); } @@ -730,7 +732,7 @@ namespace Exiv2 #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Iptc metadata (length: " - << boxData.size_ << ")\n"; + << boxData.size_ << std::endl; #endif if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21); } @@ -741,7 +743,7 @@ namespace Exiv2 if (XmpParser::encode(xmpPacket_, xmpData_) > 1) { #ifndef SUPPRESS_WARNINGS - EXV_ERROR << "Failed to encode XMP metadata.\n"; + EXV_ERROR << "Failed to encode XMP metadata." << std::endl; #endif } } @@ -760,7 +762,7 @@ namespace Exiv2 #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with XMP metadata (length: " - << boxData.size_ << ")\n"; + << boxData.size_ << ")" << std::endl; #endif if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21); } @@ -773,25 +775,25 @@ namespace Exiv2 if(memcmp(boxBuf.pData_ + 8, kJp2UuidExif, 16) == 0) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box" << std::endl; #endif } else if(memcmp(boxBuf.pData_ + 8, kJp2UuidIptc, 16) == 0) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box" << std::endl; #endif } else if(memcmp(boxBuf.pData_ + 8, kJp2UuidXmp, 16) == 0) { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box" << std::endl; #endif } else { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (length: " << box.length << ")\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (length: " << box.length << ")" << std::endl; #endif if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21); } @@ -801,7 +803,7 @@ namespace Exiv2 default: { #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (length: " << box.length << ")\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (length: " << box.length << ")" << std::endl; #endif if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21); @@ -811,7 +813,7 @@ namespace Exiv2 } #ifdef DEBUG - std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF\n"; + std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF" << std::endl; #endif } // Jp2Image::doWriteMetadata diff --git a/test/data/Reagan2.jp2 b/test/data/Reagan2.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..fa25ec449b5c5d2efa96dc794b7c8b4fd3466c94 GIT binary patch literal 8389 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 z z84{P4DcK9surlLjZ5gyIy|1G{Yfs;B&FQi_lN=Sh;(p@G4wb84FFXloXd_>r0Uf6` zj^&TDx2MpVn|-Mb=wWHu(K0W~@pP7+QFe$g^y(f5w`=F{h%Gn@ZBvsW%`Dbsf1S4c_R3eiViALTU_Un^=^dEmv0Qa32H7 zR02v%EZ9FiWp;hzoRsDcbglgUGm^KJOW=e5u{4aIvVU#pk@TZw^jitV6TOg_kqv8uE80x34`~3PTR}A85A$4_&oz?d|XCz5|ecGC?(~9;4 z(~{`7yr3~RvbfV^2scM?g8pv#h7TtX?`pHUu{YYQ7vs$e#TTK$GnCzn!X-#qRvk$N zynU*={-1Sq<+JeJU8`C2)*W5W3dWu0H~`v98OaOXMw z1($pt&Tx0jq)G7p2kh9%JZ-JrOYNQ|Qzlag(SR?Sh&olifXYArOECmtw~2Jmvc8cHK-Zv zvyF}HR>u(k*6uS@oEQj-_eXhdK|;kSA0Lt>l7I5MU!;xng03ojV9A*F*@2n4C1qbQ zj`wQw__nSG=kS$len>(STsv)g#`83q5BQwiEcR36mi&ZPi10siu1vD zipR4Z4J5IiTJhDsqBZPXWrS{8NDs<3$-RD0QSqK>g=WJK;?m%qfk)XPZTFnZr9Pjf z==n+iaO)gZe{pa7DzxvALb_w>IAwAUE34=7Lngyihi`^%so%YcP>#4SXSC1cV?5Wa z8QN0cgx4KiK3f7u2wU0Lzu?c6HU%C&>5L*h)8X5A+bJB|ox2&)t;~DXIucAsYeLwt zyt(j5%3K_kj_EKQ)_6W;7*3gq>XW=FjN%nxD%bV>#F8vJ>b8OC(=F2u6O z?WND}mZsCSenwDI))eJLs4(<4JLfVnmz>-lyTjVlvUQ%D#~R}l{dnX+zG!wioVGnf zps4sql=5&7DjOQc;!;YLLi6T;k`QRP@?Q)uzWpx9`Ba|tXepdW+Bq-!OsIfrwO(<x2-izj5MJ8ZZwTl`)Ra@ibg~EhG zbCu0>$2*9IKzZton)}jvx^zyZcDFZ=GgubG8ut=uM9^*il53IQ?1=UoECQ z`(wg|kP9Evrq#z>vvqR{DIF%~!W&%5^d1R z3SubVyN}1|O1`Z^Hk745QQCWkRIL$s zGxw&$qXjGBtB|Z;;sqV0kt^FBD;34UHaoOdz2o6+?p`JS@~+%IE34QvRT3{-Ad999 z8z0R37hMT5LMu~}KaI-oWsasY7*Qo#cQ|=mGeyJIdTDU+myTPdnuTM>H^>ZE;q#(N zjr025)eQZy^&)4yN0G4HY4qoRjP^vAv!qTBE6%5QpV#`-NRKiwE+;vd%C3dr-j@%CvssYVkGr(TUH*RS?rv{MT?Z@wclT z$2_5HBhh8a-v}~_CW{qH{ZP2ysg|T?HXQym-H?(nz;~@RW{kLVU)mu>=NKDx<_)yn zQ`i^ZSX%uO-Tp=Y+eRWrWOgRjYA|C2Na>Khl6j$F5h-uDALlnr2c>wIMz;Q{Sm{$` zq*BNu#-r8MwaNf{U3-1!C_Ua1>yc87c<6+X$K75z0u2$hS9h;&E=PBV}wZTJs< zEU1z19C4mXUG!GPir~f@W%SR69S~(H+($BzO;k@cgv z^JALfAXI9g+6ug46L@^CkOS?k|WOq4AFD`N>*&WXyp&RdPV%