Canon cr3 previews (#1958)

* Extract THMB and PRVW images from Canon CR3 file

* Added test for Canon CR3 preview extraction.

Added test data Canon-R6-pruned.CR3 (first 492016 bytes of https://raw.pixls.us/getfile.php/4659/nice/Canon%20-%20Canon%20EOS%20R6%20-%203:2.CR3).

See https://github.com/Exiv2/exiv2/issues/1893

* Fixed format specifier

* Update src/bmffimage.cpp

Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com>

* Update src/bmffimage.cpp

Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com>

* retrigger checks

Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com>
main
David Houlder 4 years ago committed by GitHub
parent bbfbcb6992
commit b385f2db1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -106,6 +106,28 @@ namespace Exiv2
void parseXmp(uint64_t length,uint64_t start); void parseXmp(uint64_t length,uint64_t start);
//@} //@}
//@{
/*!
@brief Parse a Canon PRVW or THMB box and add an entry to the set
of native previews.
@param data Buffer containing the box
@param out Logging stream
@param bTrace Controls logging
@param width_offset Index of image width field in data
@param height_offset Index of image height field in data
@param size_offset Index of image size field in data
@param relative_position Location of the start of image data in the file,
relative to the current file position indicator.
*/
void parseCr3Preview(DataBuf &data,
std::ostream &out,
bool bTrace,
uint16_t width_offset,
uint16_t height_offset,
uint32_t size_offset,
uint16_t relative_position);
//@}
//! @name Manipulators //! @name Manipulators
//@{ //@{
void readMetadata() override /* override */; void readMetadata() override /* override */;

@ -75,7 +75,9 @@
#define TAG_cmt4 0x434D5434 /**< "CMT4" gpsID */ #define TAG_cmt4 0x434D5434 /**< "CMT4" gpsID */
#define TAG_colr 0x636f6c72 /**< "colr" */ #define TAG_colr 0x636f6c72 /**< "colr" */
#define TAG_exif 0x45786966 /**< "Exif" Used by JXL*/ #define TAG_exif 0x45786966 /**< "Exif" Used by JXL*/
#define TAG_xml 0x786d6c20 /**< "xml" Used by JXL*/ #define TAG_xml 0x786d6c20 /**< "xml " Used by JXL*/
#define TAG_thmb 0x54484d42 /**< "THMB" Canon thumbnail */
#define TAG_prvw 0x50525657 /**< "PRVW" Canon preview image */
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
@ -429,7 +431,12 @@ namespace Exiv2
out << " uuidName " << name << std::endl; out << " uuidName " << name << std::endl;
bLF = false; bLF = false;
} }
if (name == "cano") { if (name == "cano" || name == "canp" ) {
if (name == "canp") {
// based on
// https://github.com/lclevy/canon_cr3/blob/7be75d6/parse_cr3.py#L271
io_->seek(8, BasicIo::cur);
}
while (io_->tell() < box_end) { while (io_->tell() < box_end) {
io_->seek(boxHandler(out,option,box_end,depth + 1), BasicIo::beg); io_->seek(boxHandler(out,option,box_end,depth + 1), BasicIo::beg);
} }
@ -456,10 +463,16 @@ namespace Exiv2
case TAG_xml: case TAG_xml:
parseXmp(box_length,io_->tell()); parseXmp(box_length,io_->tell());
break; break;
case TAG_thmb:
parseCr3Preview(data, out, bTrace, 4, 6, 8, 16);
break;
case TAG_prvw:
parseCr3Preview(data, out, bTrace, 6, 8, 12, 16);
break;
default: break ; /* do nothing */ default: break ; /* do nothing */
} }
if ( bLF&& bTrace) out << std::endl; if (bLF && bTrace) out << std::endl;
// return address of next box // return address of next box
return box_end; return box_end;
@ -540,6 +553,36 @@ namespace Exiv2
} }
} }
void BmffImage::parseCr3Preview(DataBuf &data,
std::ostream& out,
bool bTrace,
uint16_t width_offset,
uint16_t height_offset,
uint32_t size_offset,
uint16_t relative_position)
{
// Derived from https://github.com/lclevy/canon_cr3
NativePreview nativePreview;
long here = io_->tell();
enforce(here >= 0 &&
here <= std::numeric_limits<long>::max() - relative_position,
kerCorruptedMetadata);
nativePreview.position_ = here + relative_position;
nativePreview.width_ = data.read_uint16(width_offset, endian_);
nativePreview.height_ = data.read_uint16(height_offset, endian_);
nativePreview.size_ = data.read_uint32(size_offset, endian_);
nativePreview.filter_ = "";
nativePreview.mimeType_ = "image/jpeg";
nativePreviews_.push_back(nativePreview);
if (bTrace) {
out << Internal::stringFormat("width,height,size = %u,%u,%u",
nativePreview.width_,
nativePreview.height_,
nativePreview.size_);
}
}
void BmffImage::setComment(const std::string& /*comment*/) void BmffImage::setComment(const std::string& /*comment*/)
{ {
// bmff files are read-only // bmff files are read-only

Binary file not shown.

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
import system_tests
import unittest
from tempfile import TemporaryDirectory
import shutil
import hashlib
import os
# test needs system_tests.BT.vv['enable_bmff']=1
bSkip=system_tests.BT.verbose_version().get('enable_bmff')!='1'
if bSkip:
raise unittest.SkipTest('*** requires enable_bmff=1 ***')
file_basename = 'Canon-R6-pruned.CR3'
previews_expected = (
('Canon-R6-pruned-preview1.jpg', 'a182ef12ac883309b4dfc66b87eac1891286d3ae'),
('Canon-R6-pruned-preview2.jpg', '524a07f1797854e349ae140e2682ba37147fa6b2')
)
class issue_1893_cr3_preview(metaclass=system_tests.CaseMeta):
"""
Check that THMB and PRVW images are extracted from Canon CR3 files
"""
url = "https://github.com/Exiv2/exiv2/issues/1893"
filename = "$data_path/" + file_basename
commands=[] # see setUp()
if bSkip:
retval=[]
stdin=[]
stderr=[]
stdout=[]
print("*** test skipped. requires enable_bmff=1***")
else:
retval = [ 0, 0]
stderr = [ "",""]
stdin = [ "", ""]
stdout = ["""Preview 1: image/jpeg, 160x120 pixels, 16005 bytes
Preview 2: image/jpeg, 1620x1080 pixels, 389450 bytes
""", ""]
def post_tests_hook(self):
if self.commands:
for j, sha1 in previews_expected:
p = os.path.join(self.preview_image_tmp_dir.name, j)
self.assertTrue(os.path.isfile(p))
h = hashlib.sha1(open(p, 'rb').read()).hexdigest()
self.assertEqual(h, sha1)
def setUp(self):
if bSkip:
return
# Avoid polluting the test data directory with extracted previews
self.preview_image_tmp_dir = TemporaryDirectory()
shutil.copy(self.expand_variables(self.filename),
self.preview_image_tmp_dir.name)
p = os.path.join(
self.preview_image_tmp_dir.name,
file_basename)
self.commands = [
self.expand_variables("$exiv2 -pp ") + p,
self.expand_variables("$exiv2 -ep ") + p
]
Loading…
Cancel
Save