1 // ***************************************************************** -*- C++ -*- 2 /* 3 * Copyright (C) 2004-2017 Andreas Huggel <ahuggel@gmx.net> 4 * 5 * This program is part of the Exiv2 distribution. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. 20 */ 21 /* 22 File: cr2image.cpp 23 Version: $Rev: 4736 $ 24 */ 25 // ***************************************************************************** 26 #include "rcsid_int.hpp" 27 EXIV2_RCSID("@(#) $Id: cr2image.cpp 4736 2017-03-15 21:30:55Z robinwmills $") 28 29 // included header files 30 #include "config_exiv2.h" 31 32 #include "cr2image.hpp" 33 #include "tiffimage.hpp" 34 #include "cr2image_int.hpp" 35 #include "tiffcomposite_int.hpp" 36 #include "tiffimage_int.hpp" 37 #include "image.hpp" 38 #include "error.hpp" 39 #include "futils.hpp" 40 #include "i18n.h" // NLS support. 41 42 // + standard includes 43 #include <iostream> 44 #include <iomanip> 45 #include <cassert> 46 #include <cstring> 47 48 // ***************************************************************************** 49 // class member definitions 50 namespace Exiv2 { 51 52 using namespace Internal; 53 Cr2Image(BasicIo::AutoPtr io,bool)54 Cr2Image::Cr2Image(BasicIo::AutoPtr io, bool /*create*/) 55 : Image(ImageType::cr2, mdExif | mdIptc | mdXmp, io) 56 { 57 } // Cr2Image::Cr2Image 58 mimeType() const59 std::string Cr2Image::mimeType() const 60 { 61 return "image/x-canon-cr2"; 62 } 63 pixelWidth() const64 int Cr2Image::pixelWidth() const 65 { 66 ExifData::const_iterator imageWidth = exifData_.findKey(Exiv2::ExifKey("Exif.Photo.PixelXDimension")); 67 if (imageWidth != exifData_.end() && imageWidth->count() > 0) { 68 return imageWidth->toLong(); 69 } 70 return 0; 71 } 72 pixelHeight() const73 int Cr2Image::pixelHeight() const 74 { 75 ExifData::const_iterator imageHeight = exifData_.findKey(Exiv2::ExifKey("Exif.Photo.PixelYDimension")); 76 if (imageHeight != exifData_.end() && imageHeight->count() > 0) { 77 return imageHeight->toLong(); 78 } 79 return 0; 80 } 81 printStructure(std::ostream & out,Exiv2::PrintStructureOption option,int depth)82 void Cr2Image::printStructure(std::ostream& out, Exiv2::PrintStructureOption option,int depth) 83 { 84 if (io_->open() != 0) throw Error(9, io_->path(), strError()); 85 io_->seek(0,BasicIo::beg); 86 printTiffStructure(io(),out,option,depth-1); 87 } 88 setComment(const std::string &)89 void Cr2Image::setComment(const std::string& /*comment*/) 90 { 91 // not supported 92 throw(Error(32, "Image comment", "CR2")); 93 } 94 readMetadata()95 void Cr2Image::readMetadata() 96 { 97 #ifdef DEBUG 98 std::cerr << "Reading CR2 file " << io_->path() << "\n"; 99 #endif 100 if (io_->open() != 0) { 101 throw Error(9, io_->path(), strError()); 102 } 103 IoCloser closer(*io_); 104 // Ensure that this is the correct image type 105 if (!isCr2Type(*io_, false)) { 106 if (io_->error() || io_->eof()) throw Error(14); 107 throw Error(3, "CR2"); 108 } 109 clearMetadata(); 110 std::ofstream devnull; 111 printStructure(devnull, kpsRecursive, 0); 112 ByteOrder bo = Cr2Parser::decode(exifData_, 113 iptcData_, 114 xmpData_, 115 io_->mmap(), 116 io_->size()); 117 setByteOrder(bo); 118 } // Cr2Image::readMetadata 119 writeMetadata()120 void Cr2Image::writeMetadata() 121 { 122 #ifdef DEBUG 123 std::cerr << "Writing CR2 file " << io_->path() << "\n"; 124 #endif 125 ByteOrder bo = byteOrder(); 126 byte* pData = 0; 127 long size = 0; 128 IoCloser closer(*io_); 129 if (io_->open() == 0) { 130 // Ensure that this is the correct image type 131 if (isCr2Type(*io_, false)) { 132 pData = io_->mmap(true); 133 size = io_->size(); 134 Cr2Header cr2Header; 135 if (0 == cr2Header.read(pData, 16)) { 136 bo = cr2Header.byteOrder(); 137 } 138 } 139 } 140 if (bo == invalidByteOrder) { 141 bo = littleEndian; 142 } 143 setByteOrder(bo); 144 Cr2Parser::encode(*io_, pData, size, bo, exifData_, iptcData_, xmpData_); // may throw 145 } // Cr2Image::writeMetadata 146 decode(ExifData & exifData,IptcData & iptcData,XmpData & xmpData,const byte * pData,uint32_t size)147 ByteOrder Cr2Parser::decode( 148 ExifData& exifData, 149 IptcData& iptcData, 150 XmpData& xmpData, 151 const byte* pData, 152 uint32_t size 153 ) 154 { 155 Cr2Header cr2Header; 156 return TiffParserWorker::decode(exifData, 157 iptcData, 158 xmpData, 159 pData, 160 size, 161 Tag::root, 162 TiffMapping::findDecoder, 163 &cr2Header); 164 } 165 encode(BasicIo & io,const byte * pData,uint32_t size,ByteOrder byteOrder,const ExifData & exifData,const IptcData & iptcData,const XmpData & xmpData)166 WriteMethod Cr2Parser::encode( 167 BasicIo& io, 168 const byte* pData, 169 uint32_t size, 170 ByteOrder byteOrder, 171 const ExifData& exifData, 172 const IptcData& iptcData, 173 const XmpData& xmpData 174 ) 175 { 176 // Copy to be able to modify the Exif data 177 ExifData ed = exifData; 178 179 // Delete IFDs which do not occur in TIFF images 180 static const IfdId filteredIfds[] = { 181 panaRawId 182 }; 183 for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfds); ++i) { 184 #ifdef DEBUG 185 std::cerr << "Warning: Exif IFD " << filteredIfds[i] << " not encoded\n"; 186 #endif 187 ed.erase(std::remove_if(ed.begin(), 188 ed.end(), 189 FindExifdatum(filteredIfds[i])), 190 ed.end()); 191 } 192 193 std::auto_ptr<TiffHeaderBase> header(new Cr2Header(byteOrder)); 194 OffsetWriter offsetWriter; 195 offsetWriter.setOrigin(OffsetWriter::cr2RawIfdOffset, Cr2Header::offset2addr(), byteOrder); 196 return TiffParserWorker::encode(io, 197 pData, 198 size, 199 ed, 200 iptcData, 201 xmpData, 202 Tag::root, 203 TiffMapping::findEncoder, 204 header.get(), 205 &offsetWriter); 206 } 207 208 // ************************************************************************* 209 // free functions newCr2Instance(BasicIo::AutoPtr io,bool create)210 Image::AutoPtr newCr2Instance(BasicIo::AutoPtr io, bool create) 211 { 212 Image::AutoPtr image(new Cr2Image(io, create)); 213 if (!image->good()) { 214 image.reset(); 215 } 216 return image; 217 } 218 isCr2Type(BasicIo & iIo,bool advance)219 bool isCr2Type(BasicIo& iIo, bool advance) 220 { 221 const int32_t len = 16; 222 byte buf[len]; 223 iIo.read(buf, len); 224 if (iIo.error() || iIo.eof()) { 225 return false; 226 } 227 Cr2Header header; 228 bool rc = header.read(buf, len); 229 if (!advance || !rc) { 230 iIo.seek(-len, BasicIo::cur); 231 } 232 return rc; 233 } 234 235 } // namespace Exiv2 236 237 namespace Exiv2 { 238 namespace Internal { 239 240 const char* Cr2Header::cr2sig_ = "CR\2\0"; 241 Cr2Header(ByteOrder byteOrder)242 Cr2Header::Cr2Header(ByteOrder byteOrder) 243 : TiffHeaderBase(42, 16, byteOrder, 0x00000010), 244 offset2_(0x00000000) 245 { 246 } 247 ~Cr2Header()248 Cr2Header::~Cr2Header() 249 { 250 } 251 read(const byte * pData,uint32_t size)252 bool Cr2Header::read(const byte* pData, uint32_t size) 253 { 254 if (size < 16) return false; 255 256 if (pData[0] == 'I' && pData[0] == pData[1]) { 257 setByteOrder(littleEndian); 258 } 259 else if (pData[0] == 'M' && pData[0] == pData[1]) { 260 setByteOrder(bigEndian); 261 } 262 else { 263 return false; 264 } 265 if (tag() != getUShort(pData + 2, byteOrder())) return false; 266 setOffset(getULong(pData + 4, byteOrder())); 267 if (0 != memcmp(pData + 8, cr2sig_, 4)) return false; 268 offset2_ = getULong(pData + 12, byteOrder()); 269 270 return true; 271 } // Cr2Header::read 272 write() const273 DataBuf Cr2Header::write() const 274 { 275 DataBuf buf(16); 276 switch (byteOrder()) { 277 case littleEndian: 278 buf.pData_[0] = 'I'; 279 break; 280 case bigEndian: 281 buf.pData_[0] = 'M'; 282 break; 283 case invalidByteOrder: 284 assert(false); 285 break; 286 } 287 buf.pData_[1] = buf.pData_[0]; 288 289 us2Data(buf.pData_ + 2, tag(), byteOrder()); 290 ul2Data(buf.pData_ + 4, 0x00000010, byteOrder()); 291 memcpy(buf.pData_ + 8, cr2sig_, 4); 292 // Write a dummy value for the RAW IFD offset. The offset-writer is used to set this offset in a second pass. 293 ul2Data(buf.pData_ + 12, 0x00000000, byteOrder()); 294 return buf; 295 } // Cr2Header::write 296 isImageTag(uint16_t tag,IfdId group,const PrimaryGroups *) const297 bool Cr2Header::isImageTag(uint16_t tag, IfdId group, const PrimaryGroups* /*pPrimaryGroups*/) const 298 { 299 // CR2 image tags are all IFD2 and IFD3 tags 300 if (group == ifd2Id || group == ifd3Id) return true; 301 // ...and any (IFD0) tag that is in the TIFF image tags list 302 return isTiffImageTag(tag, group); 303 } 304 305 }} // namespace Internal, Exiv2 306