def test_makernote_types(self): # Makernote tags not attached to an image have an Undefined type by # default. When read from an existing image though, their type should be # correctly set (see https://bugs.launchpad.net/pyexiv2/+bug/781464). tag1 = ExifTag('Exif.Pentax.PreviewResolution') tag1.raw_value = '640 480' self.assertEqual(tag1.type, 'Undefined') self.failUnlessRaises(ValueError, getattr, tag1, 'value') tag2 = ExifTag('Exif.Pentax.CameraInfo') tag2.raw_value = '76830 20070527 2 1 4228109' self.assertEqual(tag2.type, 'Undefined') self.failUnlessRaises(ValueError, getattr, tag2, 'value') filepath = testutils.get_absolute_file_path( os.path.join('data', 'pentax-makernote.jpg')) checksum = '646804b309a4a2d31feafe9bffc5d7f0' self.assert_(testutils.CheckFileSum(filepath, checksum)) metadata = ImageMetadata(filepath) metadata.read() tag1 = metadata[tag1.key] self.assertEqual(tag1.type, 'Short') self.assertEqual(tag1.value, [640, 480]) tag2 = metadata[tag2.key] self.assertEqual(tag2.type, 'Long') self.assertEqual(tag2.value, [76830L, 20070527L, 2L, 1L, 4228109L])
def test_write_dont_preserve_timestamps(self): stat = os.stat(self.pathname) atime = round(stat.st_atime) mtime = round(stat.st_mtime) metadata = ImageMetadata(self.pathname) metadata.read() metadata.comment = 'Yellow Submarine' time.sleep(1.1) metadata.write() stat2 = os.stat(self.pathname) atime2 = round(stat2.st_atime) mtime2 = round(stat2.st_mtime) # It is not safe to assume that atime will have been modified when the # file has been read, as it may depend on mount options (e.g. noatime, # relatime). # See discussion at http://bugs.launchpad.net/pyexiv2/+bug/624999. #self.failIfEqual(atime2, atime) self.failIfEqual(mtime2, mtime) metadata.comment = 'Yesterday' time.sleep(1.1) metadata.write(preserve_timestamps=True) stat3 = os.stat(self.pathname) atime3 = round(stat3.st_atime) mtime3 = round(stat3.st_mtime) self.failUnlessEqual(atime3, atime2) self.failUnlessEqual(mtime3, mtime2)
def jpg_with_tags(scratch_directory): """A JPEG file with several tags, used by a bunch of tests. This fixture creates the file itself and returns its pathname. The file is made read-only for safety. N.B. we use NamedTemporaryFile(delete=False) because we want to be able to close the initial file descriptor, to avoid tripping over Windows' exclusive file access rules. Cleanup is handled by the teardown of the scratch_directory fixture. """ with tempfile.NamedTemporaryFile(dir=scratch_directory, suffix='.jpg', delete=False) as fp: fp.write(EMPTY_JPG_DATA) name = fp.name # Write some metadata m = ImageMetadata(name) m.read() m['Exif.Image.Make'] = 'EASTMAN KODAK COMPANY' m['Exif.Image.DateTime'] = datetime.datetime(2009, 2, 9, 13, 33, 20) m['Iptc.Application2.Caption'] = ['blabla'] m['Iptc.Application2.DateCreated'] = [datetime.date(2004, 7, 13)] m['Xmp.dc.format'] = ('image', 'jpeg') m['Xmp.dc.subject'] = ['image', 'test', 'pyexiv2'] m.comment = 'Hello World!' m.write() del m os.chmod(name, 0o0400) # r-------- return name
def _read_image(self, filename): filepath = testutils.get_absolute_file_path( os.path.join('data', filename)) self.assert_(testutils.CheckFileSum(filepath, self.checksums[filename])) m = ImageMetadata(filepath) m.read() return m
def test_from_file_and_from_buffer(self): # from file m1 = ImageMetadata(self.filepath) m1.read() self.assertEqual(hashlib.md5(m1.buffer).hexdigest(), self.md5sum) # from buffer m2 = self._metadata_from_buffer() self.assertEqual(hashlib.md5(m2.buffer).hexdigest(), self.md5sum)
def test_from_file_and_from_buffer(smiley): # from file m1 = ImageMetadata(smiley.filepath) m1.read() assert hashlib.md5(m1.buffer).hexdigest() == smiley.md5sum # from buffer m2 = metadata_from_buffer(smiley) assert hashlib.md5(m2.buffer).hexdigest() == smiley.md5sum
def _test_add_comment(self, value): metadata = ImageMetadata(self.pathname) metadata.read() key = 'Exif.Photo.UserComment' metadata[key] = value metadata.write() metadata = ImageMetadata(self.pathname) metadata.read() self.assert_(key in metadata.exif_keys) tag = metadata[key] self.assertEqual(tag.value, value)
def test_add_comment(value, uc_empty): metadata = ImageMetadata(uc_empty) metadata.read() key = 'Exif.Photo.UserComment' metadata[key] = value metadata.write() metadata = ImageMetadata(uc_empty) metadata.read() assert key in metadata.exif_keys tag = metadata[key] assert tag.type == 'Comment' assert tag.value == value
def _test_add_comment(self, value): metadata = ImageMetadata(self.pathname) metadata.read() key = 'Exif.Photo.UserComment' metadata[key] = value metadata.write() metadata = ImageMetadata(self.pathname) metadata.read() self.assert_(key in metadata.exif_keys) tag = metadata[key] self.assertEqual(tag.type, 'Comment') self.assertEqual(tag.value, value)
def test_write_preserve_timestamps(self): stat = os.stat(self.pathname) atime = round(stat.st_atime) mtime = round(stat.st_mtime) metadata = ImageMetadata(self.pathname) metadata.read() metadata.comment = 'Yellow Submarine' time.sleep(1.1) metadata.write(preserve_timestamps=True) stat2 = os.stat(self.pathname) atime2 = round(stat2.st_atime) mtime2 = round(stat2.st_mtime) self.failUnlessEqual(atime2, atime) self.failUnlessEqual(mtime2, mtime)
def test_read_from_filename(tempdir, filename): if isinstance(filename, bytes): tempdir = tempdir.encode("ascii") filepath = os.path.join(tempdir, filename) try: with open(filepath, 'wb') as fd: fd.write(UNUSUAL_JPG_DATA) except OSError as e: # The OS might not let us create files with some of the above names. pytest.skip("could not create test file: " + str(e)) m = ImageMetadata(filepath) m.read() assert m['Exif.Image.DateTime'].value.isoformat() == UNUSUAL_JPG_DATETIME
def image_read_exif(filename): data = ImageMetadata(filename) # This reads the metadata and closes the file data.read() lens_model = None tag = 'Exif.Photo.LensModel' if has_exif_tag(data, tag): lens_model = data[tag].value else: tag = 'Exif.NikonLd3.LensIDNumber' if has_exif_tag(data, tag): lens_model = data[tag].human_value tag = 'Exif.Panasonic.LensType' if has_exif_tag(data, tag): lens_model = data[tag].value tag = 'Exif.Sony1.LensID' if has_exif_tag(data, tag): lens_model = data[tag].human_value tag = 'Exif.Minolta.LensID' if has_exif_tag(data, tag): lens_model = data[tag].human_value if lens_model is None: lens_model = 'Standard' tag = 'Exif.Photo.FocalLength' if has_exif_tag(data, tag): focal_length = float(data[tag].value) else: print("%s doesn't have Exif.Photo.FocalLength set. " % (filename) + "Please fix it manually.") tag = 'Exif.Photo.FNumber' if has_exif_tag(data, tag): aperture = float(data[tag].value) else: print("%s doesn't have Exif.Photo.FNumber set. " % (filename) + "Please fix it manually.") return { "lens_model" : lens_model, "focal_length" : focal_length, "aperture" : aperture }
def setUp(self): # Create an empty image file fd, self.pathname = tempfile.mkstemp(suffix='.jpg') os.write(fd, EMPTY_JPG_DATA) os.close(fd) # Write some metadata m = ImageMetadata(self.pathname) m.read() m['Exif.Image.Make'] = 'EASTMAN KODAK COMPANY' m['Exif.Image.DateTime'] = datetime.datetime(2009, 2, 9, 13, 33, 20) m['Iptc.Application2.Caption'] = ['blabla'] m['Iptc.Application2.DateCreated'] = [datetime.date(2004, 7, 13)] m['Xmp.dc.format'] = ('image', 'jpeg') m['Xmp.dc.subject'] = ['image', 'test', 'pyexiv2'] m.comment = 'Hello World!' m.write() self.metadata = ImageMetadata(self.pathname)
def add_metadata(image, meta_dict, path_to_data_dir="", filename=None, delete_file=True): """Add meta data to an given image. Process: uuid is generated for a unique filename. Image will be stored locally, because ImageMetadata object need to receive filepath. TODO: Might work without save image on local storage. :param image: Pillow Image which where meta information should be stored in :type image: PIL.Image :param meta_dict: Dict with meta information. E.g. meta_dict={"color":["red","green"], "hex":["#ff0000", "#00ff00"]} :type meta_dict: [type] :param path_to_data_dir: Optional path of image_file. Local file will be removed within function anyway, therefore this paramter is not impportant. :type path_to_data_dir: str :return: [description] :rtype: [type] """ Path(path_to_data_dir).mkdir(parents=True, exist_ok=True) # download file to write new meta data to file if not filename: media_guid = uuid.uuid4().hex filename = path_to_data_dir + media_guid + '.jpg' image.save(filename) # add metadata meta = ImageMetadata(filename) meta.read() meta['Exif.Photo.UserComment'] = json.dumps(meta_dict) meta.write() # transform back to PIL Image byteio = BytesIO(meta.buffer) image = Image.open(byteio) # delete file if delete_file: os.remove(filename) # return image with meta information return image
def edit_exif(self): """Manipulations with exif (adding copyright).""" from pyexiv2.metadata import ImageMetadata from pyexiv2.exif import ExifValueError, ExifTag info_to_exif = ('File was downloaded from gallery {0}. \n' 'URL of image: {1} \n' 'Author: {2}'.format( url, self.image_url, self.author)) try: metadata = ImageMetadata(self.filename) metadata.read() if 'Exif.Image.Copyright' in metadata: info_to_exif = metadata['Exif.Image.Copyright'].value + '\n' + info_to_exif metadata['Exif.Image.Copyright'] = info_to_exif metadata.write() except ExifValueError, info: logging.debug('error parsing Exit. %s' % info) self.error = True
def read_metadata(path, photo, prefix="orig_"): photo[prefix + "orientation"] = 1 photo[prefix + "original_orientation"] = 1 try: meta = ImageMetadata(path) meta.read() try: photo[prefix + "orientation"] = meta["Exif.Image.Orientation"].value photo[prefix + "original_orientation"] = meta["Exif.Image.Orientation"].value except KeyError: print _log.debug("Failed to read the orientation from %s" % path) exposure_dt = meta["Exif.Image.DateTime"].value photo[prefix + "exposure_time"] = exif_datetime_to_time(exposure_dt) except KeyError: pass except Exception: print _log.exception("Failed to read date from %s", path) raise
def test_makernote_types(self): # Makernote tags not attached to an image have an Undefined type by # default. When read from an existing image though, their type should be # correctly set (see https://bugs.launchpad.net/pyexiv2/+bug/781464). tag1 = ExifTag('Exif.Pentax.PreviewResolution') tag1.raw_value = '640 480' self.assertEqual(tag1.type, 'Undefined') self.failUnlessRaises(ValueError, getattr, tag1, 'value') tag2 = ExifTag('Exif.Pentax.CameraInfo') tag2.raw_value = '76830 20070527 2 1 4228109' self.assertEqual(tag2.type, 'Undefined') self.failUnlessRaises(ValueError, getattr, tag2, 'value') filepath = testutils.get_absolute_file_path(os.path.join('data', 'pentax-makernote.jpg')) checksum = '646804b309a4a2d31feafe9bffc5d7f0' self.assert_(testutils.CheckFileSum(filepath, checksum)) metadata = ImageMetadata(filepath) metadata.read() tag1 = metadata[tag1.key] self.assertEqual(tag1.type, 'Short') self.assertEqual(tag1.value, [640, 480]) tag2 = metadata[tag2.key] self.assertEqual(tag2.type, 'Long') self.assertEqual(tag2.value, [76830L, 20070527L, 2L, 1L, 4228109L])
def read_metadata(path, photo, prefix="orig_"): photo[prefix + "orientation"] = 1 photo[prefix + "original_orientation"] = 1 try: meta = ImageMetadata(path) meta.read() try: photo[prefix + "orientation"] = meta[ "Exif.Image.Orientation"].value photo[prefix + "original_orientation"] = meta[ "Exif.Image.Orientation"].value except KeyError: print _log.debug("Failed to read the orientation from %s" % path) exposure_dt = meta["Exif.Image.DateTime"].value photo[prefix + "exposure_time"] = exif_datetime_to_time(exposure_dt) except KeyError: pass except Exception: print _log.exception("Failed to read date from %s", path) raise
def _read_image(self, filename): filepath = testutils.get_absolute_file_path(os.path.join('data', filename)) self.assert_(testutils.CheckFileSum(filepath, self.checksums[filename])) m = ImageMetadata(filepath) m.read() return m
def _test_filename(self, filename): self._create_file(filename) m = ImageMetadata(filename) m.read() os.remove(filename)
def test_read_nonexistent_file(empty_directory): non_jpg = os.path.join(empty_directory, 'non.jpg') m = ImageMetadata(non_jpg) with pytest.raises(OSError): m.read()
class TestImageMetadata(unittest.TestCase): def setUp(self): # Create an empty image file fd, self.pathname = tempfile.mkstemp(suffix='.jpg') os.write(fd, EMPTY_JPG_DATA) os.close(fd) # Write some metadata m = ImageMetadata(self.pathname) m.read() m['Exif.Image.Make'] = 'EASTMAN KODAK COMPANY' m['Exif.Image.DateTime'] = datetime.datetime(2009, 2, 9, 13, 33, 20) m['Iptc.Application2.Caption'] = ['blabla'] m['Iptc.Application2.DateCreated'] = [datetime.date(2004, 7, 13)] m['Xmp.dc.format'] = ('image', 'jpeg') m['Xmp.dc.subject'] = ['image', 'test', 'pyexiv2'] m.comment = 'Hello World!' m.write() self.metadata = ImageMetadata(self.pathname) def tearDown(self): os.remove(self.pathname) ###################### # Test general methods ###################### def test_not_read_raises(self): # http://bugs.launchpad.net/pyexiv2/+bug/687373 self.assertRaises(IOError, self.metadata.write) self.assertRaises(IOError, getattr, self.metadata, 'dimensions') self.assertRaises(IOError, getattr, self.metadata, 'mime_type') self.assertRaises(IOError, getattr, self.metadata, 'exif_keys') self.assertRaises(IOError, getattr, self.metadata, 'iptc_keys') self.assertRaises(IOError, getattr, self.metadata, 'xmp_keys') self.assertRaises(IOError, self.metadata._get_exif_tag, 'Exif.Image.Make') self.assertRaises(IOError, self.metadata._get_iptc_tag, 'Iptc.Application2.Caption') self.assertRaises(IOError, self.metadata._get_xmp_tag, 'Xmp.dc.format') self.assertRaises(IOError, self.metadata._set_exif_tag, 'Exif.Image.Make', 'foobar') self.assertRaises(IOError, self.metadata._set_iptc_tag, 'Iptc.Application2.Caption', ['foobar']) self.assertRaises(IOError, self.metadata._set_xmp_tag, 'Xmp.dc.format', ('foo', 'bar')) self.assertRaises(IOError, self.metadata._delete_exif_tag, 'Exif.Image.Make') self.assertRaises(IOError, self.metadata._delete_iptc_tag, 'Iptc.Application2.Caption') self.assertRaises(IOError, self.metadata._delete_xmp_tag, 'Xmp.dc.format') self.assertRaises(IOError, getattr, self.metadata, 'comment') self.assertRaises(IOError, setattr, self.metadata, 'comment', 'foobar') self.assertRaises(IOError, delattr, self.metadata, 'comment') self.assertRaises(IOError, getattr, self.metadata, 'previews') other = ImageMetadata(self.pathname) self.assertRaises(IOError, self.metadata.copy, other) thumb = self.metadata.exif_thumbnail self.assertRaises(IOError, getattr, thumb, 'mime_type') self.assertRaises(IOError, getattr, thumb, 'extension') self.assertRaises(IOError, thumb.write_to_file, '/tmp/foobar.jpg') self.assertRaises(IOError, thumb.erase) self.assertRaises(IOError, thumb.set_from_file, '/tmp/foobar.jpg') self.assertRaises(IOError, getattr, self.metadata, 'iptc_charset') def test_read(self): self.assertRaises(IOError, getattr, self.metadata, '_image') self.metadata.read() self.failIfEqual(self.metadata._image, None) def test_read_nonexistent_file(self): metadata = ImageMetadata('idontexist') self.failUnlessRaises(IOError, metadata.read) def test_write_preserve_timestamps(self): stat = os.stat(self.pathname) atime = round(stat.st_atime) mtime = round(stat.st_mtime) metadata = ImageMetadata(self.pathname) metadata.read() metadata.comment = 'Yellow Submarine' time.sleep(1.1) metadata.write(preserve_timestamps=True) stat2 = os.stat(self.pathname) atime2 = round(stat2.st_atime) mtime2 = round(stat2.st_mtime) self.failUnlessEqual(atime2, atime) self.failUnlessEqual(mtime2, mtime) def test_write_dont_preserve_timestamps(self): stat = os.stat(self.pathname) atime = round(stat.st_atime) mtime = round(stat.st_mtime) metadata = ImageMetadata(self.pathname) metadata.read() metadata.comment = 'Yellow Submarine' time.sleep(1.1) metadata.write() stat2 = os.stat(self.pathname) atime2 = round(stat2.st_atime) mtime2 = round(stat2.st_mtime) # It is not safe to assume that atime will have been modified when the # file has been read, as it may depend on mount options (e.g. noatime, # relatime). # See discussion at http://bugs.launchpad.net/pyexiv2/+bug/624999. #self.failIfEqual(atime2, atime) self.failIfEqual(mtime2, mtime) metadata.comment = 'Yesterday' time.sleep(1.1) metadata.write(preserve_timestamps=True) stat3 = os.stat(self.pathname) atime3 = round(stat3.st_atime) mtime3 = round(stat3.st_mtime) self.failUnlessEqual(atime3, atime2) self.failUnlessEqual(mtime3, mtime2) ########################### # Test EXIF-related methods ########################### def test_exif_keys(self): self.metadata.read() self.assertEqual(self.metadata._keys['exif'], None) keys = self.metadata.exif_keys self.assertEqual(len(keys), 2) self.assertEqual(self.metadata._keys['exif'], keys) def test_get_exif_tag(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Get an existing tag key = 'Exif.Image.Make' tag = self.metadata._get_exif_tag(key) self.assert_(isinstance(tag, ExifTag)) self.assertEqual(self.metadata._tags['exif'][key], tag) # Try to get an nonexistent tag key = 'Exif.Photo.Sharpness' self.failUnlessRaises(KeyError, self.metadata._get_exif_tag, key) def test_set_exif_tag_wrong(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Try to set a tag with wrong type tag = 'Not an exif tag' self.failUnlessRaises(TypeError, self.metadata._set_exif_tag, tag) self.assertEqual(self.metadata._tags['exif'], {}) def test_set_exif_tag_create(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Create a new tag tag = ExifTag('Exif.Thumbnail.Orientation', 1) self.assert_(tag.key not in self.metadata.exif_keys) self.metadata._set_exif_tag(tag.key, tag) self.assert_(tag.key in self.metadata.exif_keys) self.assertEqual(self.metadata._tags['exif'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._exifKeys()) self.assertEqual( self.metadata._image._getExifTag(tag.key)._getRawValue(), tag.raw_value) def test_set_exif_tag_overwrite(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Overwrite an existing tag tag = ExifTag('Exif.Image.DateTime', datetime.datetime(2009, 3, 20, 20, 32, 0)) self.metadata._set_exif_tag(tag.key, tag) self.assertEqual(self.metadata._tags['exif'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._exifKeys()) self.assertEqual( self.metadata._image._getExifTag(tag.key)._getRawValue(), tag.raw_value) def test_set_exif_tag_overwrite_already_cached(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Overwrite an existing tag already cached key = 'Exif.Image.Make' tag = self.metadata._get_exif_tag(key) self.assertEqual(self.metadata._tags['exif'][key], tag) new_tag = ExifTag(key, 'World Company') self.metadata._set_exif_tag(key, new_tag) self.assertEqual(self.metadata._tags['exif'], {key: new_tag}) self.assert_(key in self.metadata._image._exifKeys()) self.assertEqual( self.metadata._image._getExifTag(key)._getRawValue(), new_tag.raw_value) def test_set_exif_tag_direct_value_assignment(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Direct value assignment: pass a value instead of a fully-formed tag key = 'Exif.Thumbnail.Orientation' value = 1 self.metadata._set_exif_tag(key, value) self.assert_(key in self.metadata.exif_keys) self.assert_(key in self.metadata._image._exifKeys()) tag = self.metadata._get_exif_tag(key) self.assertEqual(tag.value, value) self.assertEqual(self.metadata._tags['exif'], {key: tag}) self.assertEqual( self.metadata._image._getExifTag(key)._getRawValue(), tag.raw_value) def test_delete_exif_tag_inexistent(self): self.metadata.read() key = 'Exif.Image.Artist' self.failUnlessRaises(KeyError, self.metadata._delete_exif_tag, key) def test_delete_exif_tag_not_cached(self): self.metadata.read() key = 'Exif.Image.DateTime' self.assertEqual(self.metadata._tags['exif'], {}) self.assert_(key in self.metadata.exif_keys) self.metadata._delete_exif_tag(key) self.assertEqual(self.metadata._tags['exif'], {}) self.failIf(key in self.metadata.exif_keys) def test_delete_exif_tag_cached(self): self.metadata.read() key = 'Exif.Image.DateTime' self.assert_(key in self.metadata.exif_keys) tag = self.metadata._get_exif_tag(key) self.assertEqual(self.metadata._tags['exif'][key], tag) self.metadata._delete_exif_tag(key) self.assertEqual(self.metadata._tags['exif'], {}) self.failIf(key in self.metadata.exif_keys) ########################### # Test IPTC-related methods ########################### def test_iptc_keys(self): self.metadata.read() self.assertEqual(self.metadata._keys['iptc'], None) keys = self.metadata.iptc_keys self.assertEqual(len(keys), 2) self.assertEqual(self.metadata._keys['iptc'], keys) def test_get_iptc_tag(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Get an existing tag key = 'Iptc.Application2.DateCreated' tag = self.metadata._get_iptc_tag(key) self.assert_(isinstance(tag, IptcTag)) self.assertEqual(self.metadata._tags['iptc'][key], tag) # Try to get an nonexistent tag key = 'Iptc.Application2.Copyright' self.failUnlessRaises(KeyError, self.metadata._get_iptc_tag, key) def test_set_iptc_tag_wrong(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Try to set a tag with wrong type tag = 'Not an iptc tag' self.failUnlessRaises(TypeError, self.metadata._set_iptc_tag, tag) self.assertEqual(self.metadata._tags['iptc'], {}) def test_set_iptc_tag_create(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Create a new tag tag = IptcTag('Iptc.Application2.Writer', ['Nobody']) self.assert_(tag.key not in self.metadata.iptc_keys) self.metadata._set_iptc_tag(tag.key, tag) self.assert_(tag.key in self.metadata.iptc_keys) self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._iptcKeys()) self.assertEqual( self.metadata._image._getIptcTag(tag.key)._getRawValues(), ['Nobody']) def test_set_iptc_tag_overwrite(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Overwrite an existing tag tag = IptcTag('Iptc.Application2.Caption', ['A picture.']) self.metadata._set_iptc_tag(tag.key, tag) self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._iptcKeys()) self.assertEqual( self.metadata._image._getIptcTag(tag.key)._getRawValues(), ['A picture.']) def test_set_iptc_tag_overwrite_already_cached(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Overwrite an existing tag already cached key = 'Iptc.Application2.Caption' tag = self.metadata._get_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'][key], tag) new_tag = IptcTag(key, ['A picture.']) self.metadata._set_iptc_tag(key, new_tag) self.assertEqual(self.metadata._tags['iptc'], {key: new_tag}) self.assert_(key in self.metadata._image._iptcKeys()) self.assertEqual( self.metadata._image._getIptcTag(key)._getRawValues(), ['A picture.']) def test_set_iptc_tag_direct_value_assignment(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Direct value assignment: pass a value instead of a fully-formed tag key = 'Iptc.Application2.Writer' values = ['Nobody'] self.metadata._set_iptc_tag(key, values) self.assert_(key in self.metadata.iptc_keys) self.assert_(key in self.metadata._image._iptcKeys()) tag = self.metadata._get_iptc_tag(key) self.assertEqual(tag.value, values) self.assertEqual(self.metadata._tags['iptc'], {key: tag}) self.assertEqual( self.metadata._image._getIptcTag(key)._getRawValues(), ['Nobody']) def test_delete_iptc_tag_inexistent(self): self.metadata.read() key = 'Iptc.Application2.LocationCode' self.failUnlessRaises(KeyError, self.metadata._delete_iptc_tag, key) def test_delete_iptc_tag_not_cached(self): self.metadata.read() key = 'Iptc.Application2.Caption' self.assertEqual(self.metadata._tags['iptc'], {}) self.assert_(key in self.metadata.iptc_keys) self.metadata._delete_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'], {}) self.failIf(key in self.metadata.iptc_keys) def test_delete_iptc_tag_cached(self): self.metadata.read() key = 'Iptc.Application2.Caption' self.assert_(key in self.metadata.iptc_keys) tag = self.metadata._get_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'][key], tag) self.metadata._delete_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'], {}) self.failIf(key in self.metadata.iptc_keys) ########################## # Test XMP-related methods ########################## def test_xmp_keys(self): self.metadata.read() self.assertEqual(self.metadata._keys['xmp'], None) keys = self.metadata.xmp_keys self.assertEqual(len(keys), 2) self.assertEqual(self.metadata._keys['xmp'], keys) def test_get_xmp_tag(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Get an existing tag key = 'Xmp.dc.subject' tag = self.metadata._get_xmp_tag(key) self.assert_(isinstance(tag, XmpTag)) self.assertEqual(self.metadata._tags['xmp'][key], tag) # Try to get an nonexistent tag key = 'Xmp.xmp.Label' self.failUnlessRaises(KeyError, self.metadata._get_xmp_tag, key) def test_set_xmp_tag_wrong(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Try to set a tag with wrong type tag = 'Not an xmp tag' self.failUnlessRaises(TypeError, self.metadata._set_xmp_tag, tag) self.assertEqual(self.metadata._tags['xmp'], {}) def test_set_xmp_tag_create(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Create a new tag tag = XmpTag('Xmp.dc.title', { 'x-default': 'This is not a title', 'fr-FR': "Ceci n'est pas un titre" }) self.assert_(tag.key not in self.metadata.xmp_keys) self.metadata._set_xmp_tag(tag.key, tag) self.assert_(tag.key in self.metadata.xmp_keys) self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._xmpKeys()) self.assertEqual( self.metadata._image._getXmpTag(tag.key)._getLangAltValue(), { 'x-default': 'This is not a title', 'fr-FR': "Ceci n'est pas un titre" }) def test_set_xmp_tag_overwrite(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Overwrite an existing tag tag = XmpTag('Xmp.dc.format', ('image', 'png')) self.metadata._set_xmp_tag(tag.key, tag) self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._xmpKeys()) self.assertEqual( self.metadata._image._getXmpTag(tag.key)._getTextValue(), tag.raw_value) def test_set_xmp_tag_overwrite_already_cached(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Overwrite an existing tag already cached key = 'Xmp.dc.subject' tag = self.metadata._get_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'][key], tag) new_tag = XmpTag(key, ['hello', 'world']) self.metadata._set_xmp_tag(key, new_tag) self.assertEqual(self.metadata._tags['xmp'], {key: new_tag}) self.assert_(key in self.metadata._image._xmpKeys()) self.assertEqual( self.metadata._image._getXmpTag(key)._getArrayValue(), ['hello', 'world']) def test_set_xmp_tag_direct_value_assignment(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Direct value assignment: pass a value instead of a fully-formed tag key = 'Xmp.dc.title' value = {'x-default': 'Landscape', 'fr-FR': "Paysage"} self.metadata._set_xmp_tag(key, value) self.assert_(key in self.metadata.xmp_keys) self.assert_(key in self.metadata._image._xmpKeys()) tag = self.metadata._get_xmp_tag(key) self.assertEqual(tag.value, value) self.assertEqual(self.metadata._tags['xmp'], {key: tag}) self.assertEqual( self.metadata._image._getXmpTag(key)._getLangAltValue(), { 'x-default': 'Landscape', 'fr-FR': "Paysage" }) def test_delete_xmp_tag_inexistent(self): self.metadata.read() key = 'Xmp.xmp.CreatorTool' self.failUnlessRaises(KeyError, self.metadata._delete_xmp_tag, key) def test_delete_xmp_tag_not_cached(self): self.metadata.read() key = 'Xmp.dc.subject' self.assertEqual(self.metadata._tags['xmp'], {}) self.assert_(key in self.metadata.xmp_keys) self.metadata._delete_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'], {}) self.failIf(key in self.metadata.xmp_keys) def test_delete_xmp_tag_cached(self): self.metadata.read() key = 'Xmp.dc.subject' self.assert_(key in self.metadata.xmp_keys) tag = self.metadata._get_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'][key], tag) self.metadata._delete_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'], {}) self.failIf(key in self.metadata.xmp_keys) ########################### # Test dictionary interface ########################### def test_getitem(self): self.metadata.read() # Get existing tags key = 'Exif.Image.DateTime' tag = self.metadata[key] self.assert_(isinstance(tag, ExifTag)) key = 'Iptc.Application2.Caption' tag = self.metadata[key] self.assert_(isinstance(tag, IptcTag)) key = 'Xmp.dc.format' tag = self.metadata[key] self.assert_(isinstance(tag, XmpTag)) # Try to get nonexistent tags keys = ('Exif.Image.SamplesPerPixel', 'Iptc.Application2.FixtureId', 'Xmp.xmp.Rating', 'Wrong.Noluck.Raise') for key in keys: self.failUnlessRaises(KeyError, self.metadata.__getitem__, key) def test_setitem(self): self.metadata.read() # Set new tags key = 'Exif.Photo.ExposureBiasValue' tag = ExifTag(key, make_fraction(0, 3)) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['exif']) self.failUnlessEqual(self.metadata._tags['exif'][key], tag) key = 'Iptc.Application2.City' tag = IptcTag(key, ['Barcelona']) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['iptc']) self.failUnlessEqual(self.metadata._tags['iptc'][key], tag) key = 'Xmp.dc.description' tag = XmpTag(key, {'x-default': 'Sunset picture.'}) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['xmp']) self.failUnlessEqual(self.metadata._tags['xmp'][key], tag) # Replace existing tags key = 'Exif.Photo.ExifVersion' tag = ExifTag(key, '0220') self.metadata[key] = tag self.failUnless(key in self.metadata._tags['exif']) self.failUnlessEqual(self.metadata._tags['exif'][key], tag) key = 'Iptc.Application2.Caption' tag = IptcTag(key, ['Sunset on Barcelona.']) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['iptc']) self.failUnlessEqual(self.metadata._tags['iptc'][key], tag) key = 'Xmp.dc.subject' tag = XmpTag(key, ['sunset', 'Barcelona', 'beautiful', 'beach']) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['xmp']) self.failUnlessEqual(self.metadata._tags['xmp'][key], tag) def test_delitem(self): self.metadata.read() # Delete existing tags key = 'Exif.Image.Make' del self.metadata[key] self.failIf(key in self.metadata._keys['exif']) self.failIf(key in self.metadata._tags['exif']) key = 'Iptc.Application2.Caption' del self.metadata[key] self.failIf(key in self.metadata._keys['iptc']) self.failIf(key in self.metadata._tags['iptc']) key = 'Xmp.dc.subject' del self.metadata[key] self.failIf(key in self.metadata._keys['xmp']) self.failIf(key in self.metadata._tags['xmp']) # Try to delete nonexistent tags keys = ('Exif.Image.SamplesPerPixel', 'Iptc.Application2.FixtureId', 'Xmp.xmp.Rating', 'Wrong.Noluck.Raise') for key in keys: self.failUnlessRaises(KeyError, self.metadata.__delitem__, key) def test_replace_tag_by_itself(self): # Test that replacing an existing tag by itself # doesn’t result in an ugly segmentation fault # (see https://bugs.launchpad.net/pyexiv2/+bug/622739). self.metadata.read() keys = self.metadata.exif_keys + \ self.metadata.iptc_keys + \ self.metadata.xmp_keys for key in keys: self.metadata[key] = self.metadata[key] def test_nonexistent_tag_family(self): self.metadata.read() key = 'Bleh.Image.DateTime' self.failUnlessRaises(KeyError, self.metadata.__getitem__, key) self.failUnlessRaises(KeyError, self.metadata.__setitem__, key, datetime.date.today()) self.failUnlessRaises(KeyError, self.metadata.__delitem__, key) ########################## # Test the image comment # ########################## def test_get_comment(self): self.metadata.read() self.failUnlessEqual(self.metadata.comment, 'Hello World!') def test_set_comment(self): self.metadata.read() comment = 'Welcome to the real world.' self.metadata.comment = comment self.failUnlessEqual(self.metadata.comment, comment) self.metadata.comment = None self.failUnlessEqual(self.metadata.comment, '') def test_delete_comment(self): self.metadata.read() del self.metadata.comment self.failUnlessEqual(self.metadata.comment, '') #################### # Test metadata copy #################### def _set_up_other(self): self.other = ImageMetadata.from_buffer(EMPTY_JPG_DATA) def test_copy_metadata(self): self.metadata.read() self._set_up_other() self.other.read() families = ('exif', 'iptc', 'xmp') for family in families: self.failUnlessEqual(getattr(self.other, '%s_keys' % family), []) self.metadata.copy(self.other) for family in ('exif', 'iptc', 'xmp'): self.failUnlessEqual(self.other._keys[family], None) self.failUnlessEqual(self.other._tags[family], {}) keys = getattr(self.metadata, '%s_keys' % family) self.failUnlessEqual( getattr(self.other._image, '_%sKeys' % family)(), keys) self.failUnlessEqual(getattr(self.other, '%s_keys' % family), keys) for key in self.metadata.exif_keys: self.failUnlessEqual(self.metadata[key].value, self.other[key].value) for key in self.metadata.iptc_keys: self.failUnlessEqual(self.metadata[key].value, self.other[key].value) for key in self.metadata.xmp_keys: self.failUnlessEqual(self.metadata[key].value, self.other[key].value) self.failUnlessEqual(self.metadata.comment, self.other.comment) ############################# # Test MutableMapping methods ############################# def _set_up_clean(self): self.clean = ImageMetadata.from_buffer(EMPTY_JPG_DATA) def test_mutablemapping(self): self._set_up_clean() self.clean.read() self.assertEqual(len(self.clean), 0) self.assertTrue('Exif.Image.DateTimeOriginal' not in self.clean) key = 'Exif.Image.DateTimeOriginal' correctDate = datetime.datetime(2007, 3, 11) incorrectDate = datetime.datetime(2009, 3, 25) tag_date = ExifTag(key, correctDate) false_tag_date = ExifTag(key, incorrectDate) self.clean[key] = tag_date self.assertEqual(len(self.clean), 1) self.assertTrue('Exif.Image.DateTimeOriginal' in self.clean) self.assertEqual( self.clean.get('Exif.Image.DateTimeOriginal', false_tag_date), tag_date) self.assertEqual(self.clean.get('Exif.Image.DateTime', tag_date), tag_date) key = 'Exif.Photo.UserComment' tag = ExifTag(key, 'UserComment') self.clean[key] = tag key = 'Iptc.Application2.Caption' tag = IptcTag(key, ['Caption']) self.clean[key] = tag key = 'Xmp.dc.subject' tag = XmpTag(key, ['subject', 'values']) self.clean[key] = tag self.assertTrue('Exif.Photo.UserComment' in self.clean) self.assertTrue('Iptc.Application2.Caption' in self.clean) self.assertTrue('Xmp.dc.subject' in self.clean) self.clean.clear() self.assertEqual(len(self.clean), 0) self.assertTrue('Exif.Photo.UserComment' not in self.clean) self.assertTrue('Iptc.Application2.Caption' not in self.clean) self.assertTrue('Xmp.dc.subject' not in self.clean) ########################### # Test the EXIF thumbnail # ########################### def _test_thumbnail_tags(self, there): keys = ('Exif.Thumbnail.Compression', 'Exif.Thumbnail.JPEGInterchangeFormat', 'Exif.Thumbnail.JPEGInterchangeFormatLength') for key in keys: self.assertEqual(key in self.metadata.exif_keys, there) def test_no_exif_thumbnail(self): self.metadata.read() thumb = self.metadata.exif_thumbnail self.assertEqual(thumb.mime_type, '') self.assertEqual(thumb.extension, '') # ExifThumbnail._get_data not yet implemented #self.assertEqual(thumb.data, '') self._test_thumbnail_tags(False) def test_set_exif_thumbnail_from_data(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA self.assertEqual(thumb.mime_type, 'image/jpeg') self.assertEqual(thumb.extension, '.jpg') # ExifThumbnail._get_data not yet implemented #self.assertEqual(thumb.data, EMPTY_JPG_DATA) self._test_thumbnail_tags(True) def test_set_exif_thumbnail_from_file(self): fd, pathname = tempfile.mkstemp(suffix='.jpg') os.write(fd, EMPTY_JPG_DATA) os.close(fd) self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.set_from_file(pathname) os.remove(pathname) self.assertEqual(thumb.mime_type, 'image/jpeg') self.assertEqual(thumb.extension, '.jpg') # ExifThumbnail._get_data not yet implemented #self.assertEqual(thumb.data, EMPTY_JPG_DATA) self._test_thumbnail_tags(True) def test_write_exif_thumbnail_to_file(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA fd, pathname = tempfile.mkstemp() os.close(fd) os.remove(pathname) thumb.write_to_file(pathname) pathname = pathname + thumb.extension fd = open(pathname, 'rb') self.assertEqual(fd.read(), EMPTY_JPG_DATA) fd.close() os.remove(pathname) def test_erase_exif_thumbnail(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA self.assertEqual(thumb.mime_type, 'image/jpeg') self.assertEqual(thumb.extension, '.jpg') # ExifThumbnail._get_data not yet implemented #self.assertEqual(thumb.data, EMPTY_JPG_DATA) self._test_thumbnail_tags(True) thumb.erase() self.assertEqual(thumb.mime_type, '') self.assertEqual(thumb.extension, '') #self.assertEqual(thumb.data, '') self._test_thumbnail_tags(False) def test_set_exif_thumbnail_from_invalid_data(self): # No check on the format of the buffer is performed, therefore it will # always work. self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = 'invalid' self.assertEqual(thumb.mime_type, 'image/jpeg') self._test_thumbnail_tags(True) def test_set_exif_thumbnail_from_inexistent_file(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail fd, pathname = tempfile.mkstemp() os.close(fd) os.remove(pathname) self.failUnlessRaises(IOError, thumb.set_from_file, pathname) self._test_thumbnail_tags(False) def test_exif_thumbnail_is_preview(self): self.metadata.read() self._test_thumbnail_tags(False) self.assertEqual(len(self.metadata.previews), 0) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA self._test_thumbnail_tags(True) self.assertEqual(len(self.metadata.previews), 1) preview = self.metadata.previews[0] self.assertEqual(thumb.mime_type, preview.mime_type) self.assertEqual(thumb.extension, preview.extension) ######################### # Test the IPTC charset # ######################### def test_guess_iptc_charset(self): # If no charset is defined, exiv2 guesses it from the encoding of the # metadata. self.metadata.read() self.assertEqual(self.metadata.iptc_charset, 'ascii') self.metadata['Iptc.Application2.City'] = [u'Córdoba'] self.assertEqual(self.metadata.iptc_charset, 'utf-8') def test_set_iptc_charset_utf8(self): self.metadata.read() self.assert_( 'Iptc.Envelope.CharacterSet' not in self.metadata.iptc_keys) self.assertEqual(self.metadata.iptc_charset, 'ascii') values = ('utf-8', 'utf8', 'u8', 'utf', 'utf_8') for value in values: self.metadata.iptc_charset = value self.assertEqual(self.metadata.iptc_charset, 'utf-8') self.metadata.iptc_charset = value.upper() self.assertEqual(self.metadata.iptc_charset, 'utf-8') def test_set_invalid_iptc_charset(self): self.metadata.read() self.assert_( 'Iptc.Envelope.CharacterSet' not in self.metadata.iptc_keys) values = ('invalid', 'utf-9', '3.14') for value in values: self.assertRaises(ValueError, self.metadata.__setattr__, 'iptc_charset', value) def test_set_unhandled_iptc_charset(self): # At the moment, the only charset handled is UTF-8. self.metadata.read() self.assert_( 'Iptc.Envelope.CharacterSet' not in self.metadata.iptc_keys) values = ('ascii', 'iso8859_15', 'shift_jis') for value in values: self.assertRaises(ValueError, self.metadata.__setattr__, 'iptc_charset', value) def test_delete_iptc_charset(self): self.metadata.read() key = 'Iptc.Envelope.CharacterSet' self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys) del self.metadata.iptc_charset self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys) self.metadata.iptc_charset = 'utf-8' self.assertEqual(self.metadata.iptc_charset, 'utf-8') self.assert_(key in self.metadata.iptc_keys) del self.metadata.iptc_charset self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys) self.metadata.iptc_charset = 'utf-8' self.assertEqual(self.metadata.iptc_charset, 'utf-8') self.assert_(key in self.metadata.iptc_keys) self.metadata.iptc_charset = None self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys)
def get_metadata(): data = ImageMetadata(COPY) data.read() return data
# # Author: Olivier Tilloy <*****@*****.**> # # ****************************************************************************** import sys from pyexiv2.metadata import ImageMetadata if __name__ == '__main__': args = sys.argv if len(args) != 2: print 'Usage: %s image_file' % args[0] sys.exit(-1) metadata = ImageMetadata(args[1]) metadata.read() for key in metadata.exif_keys: tag = metadata[key] print '%-45s%-11s%s' % (key, tag.type, str(tag)) for key in metadata.iptc_keys: tag = metadata[key] print '%-45s%-11s%s' % (key, tag.type, str(tag)) # TODO: print XMP tags.
class TestImageMetadata(unittest.TestCase): def setUp(self): # Create an empty image file fd, self.pathname = tempfile.mkstemp(suffix='.jpg') os.write(fd, EMPTY_JPG_DATA) os.close(fd) # Write some metadata m = ImageMetadata(self.pathname) m.read() m['Exif.Image.Make'] = 'EASTMAN KODAK COMPANY' m['Exif.Image.DateTime'] = datetime.datetime(2009, 2, 9, 13, 33, 20) m['Iptc.Application2.Caption'] = ['blabla'] m['Iptc.Application2.DateCreated'] = [datetime.date(2004, 7, 13)] m['Xmp.dc.format'] = ('image', 'jpeg') m['Xmp.dc.subject'] = ['image', 'test', 'pyexiv2'] m.comment = 'Hello World!' m.write() self.metadata = ImageMetadata(self.pathname) def tearDown(self): os.remove(self.pathname) ###################### # Test general methods ###################### def test_not_read_raises(self): # http://bugs.launchpad.net/pyexiv2/+bug/687373 self.assertRaises(IOError, self.metadata.write) self.assertRaises(IOError, self.metadata.__getattribute__, 'dimensions') self.assertRaises(IOError, self.metadata.__getattribute__, 'mime_type') self.assertRaises(IOError, self.metadata.__getattribute__, 'exif_keys') self.assertRaises(IOError, self.metadata.__getattribute__, 'iptc_keys') self.assertRaises(IOError, self.metadata.__getattribute__, 'xmp_keys') self.assertRaises(IOError, self.metadata._get_exif_tag, 'Exif.Image.Make') self.assertRaises(IOError, self.metadata._get_iptc_tag, 'Iptc.Application2.Caption') self.assertRaises(IOError, self.metadata._get_xmp_tag, 'Xmp.dc.format') self.assertRaises(IOError, self.metadata._set_exif_tag, 'Exif.Image.Make', 'foobar') self.assertRaises(IOError, self.metadata._set_iptc_tag, 'Iptc.Application2.Caption', ['foobar']) self.assertRaises(IOError, self.metadata._set_xmp_tag, 'Xmp.dc.format', ('foo', 'bar')) self.assertRaises(IOError, self.metadata._delete_exif_tag, 'Exif.Image.Make') self.assertRaises(IOError, self.metadata._delete_iptc_tag, 'Iptc.Application2.Caption') self.assertRaises(IOError, self.metadata._delete_xmp_tag, 'Xmp.dc.format') self.assertRaises(IOError, self.metadata._get_comment) self.assertRaises(IOError, self.metadata._set_comment, 'foobar') self.assertRaises(IOError, self.metadata._del_comment) self.assertRaises(IOError, self.metadata.__getattribute__, 'previews') other = ImageMetadata(self.pathname) self.assertRaises(IOError, self.metadata.copy, other) self.assertRaises(IOError, self.metadata.__getattribute__, 'buffer') thumb = self.metadata.exif_thumbnail self.assertRaises(IOError, thumb.__getattribute__, 'mime_type') self.assertRaises(IOError, thumb.__getattribute__, 'extension') self.assertRaises(IOError, thumb.write_to_file, '/tmp/foobar.jpg') self.assertRaises(IOError, thumb.erase) self.assertRaises(IOError, thumb.set_from_file, '/tmp/foobar.jpg') self.assertRaises(IOError, thumb.__getattribute__, 'data') self.assertRaises(IOError, thumb.__setattr__, 'data', EMPTY_JPG_DATA) self.assertRaises(IOError, self.metadata.__getattribute__, 'iptc_charset') def test_read(self): self.assertRaises(IOError, self.metadata.__getattribute__, '_image') self.metadata.read() self.failIfEqual(self.metadata._image, None) def test_read_nonexistent_file(self): metadata = ImageMetadata('idontexist') self.failUnlessRaises(IOError, metadata.read) def test_write_preserve_timestamps(self): stat = os.stat(self.pathname) atime = round(stat.st_atime) mtime = round(stat.st_mtime) metadata = ImageMetadata(self.pathname) metadata.read() metadata.comment = 'Yellow Submarine' time.sleep(1.1) metadata.write(preserve_timestamps=True) stat2 = os.stat(self.pathname) atime2 = round(stat2.st_atime) mtime2 = round(stat2.st_mtime) self.failUnlessEqual(atime2, atime) self.failUnlessEqual(mtime2, mtime) def test_write_dont_preserve_timestamps(self): stat = os.stat(self.pathname) atime = round(stat.st_atime) mtime = round(stat.st_mtime) metadata = ImageMetadata(self.pathname) metadata.read() metadata.comment = 'Yellow Submarine' time.sleep(1.1) metadata.write() stat2 = os.stat(self.pathname) atime2 = round(stat2.st_atime) mtime2 = round(stat2.st_mtime) # It is not safe to assume that atime will have been modified when the # file has been read, as it may depend on mount options (e.g. noatime, # relatime). # See discussion at http://bugs.launchpad.net/pyexiv2/+bug/624999. #self.failIfEqual(atime2, atime) self.failIfEqual(mtime2, mtime) metadata.comment = 'Yesterday' time.sleep(1.1) metadata.write(preserve_timestamps=True) stat3 = os.stat(self.pathname) atime3 = round(stat3.st_atime) mtime3 = round(stat3.st_mtime) self.failUnlessEqual(atime3, atime2) self.failUnlessEqual(mtime3, mtime2) ########################### # Test EXIF-related methods ########################### def test_exif_keys(self): self.metadata.read() self.assertEqual(self.metadata._keys['exif'], None) keys = self.metadata.exif_keys self.assertEqual(len(keys), 2) self.assertEqual(self.metadata._keys['exif'], keys) def test_get_exif_tag(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Get an existing tag key = 'Exif.Image.Make' tag = self.metadata._get_exif_tag(key) self.assert_(isinstance(tag, ExifTag)) self.assertEqual(self.metadata._tags['exif'][key], tag) # Try to get an nonexistent tag key = 'Exif.Photo.Sharpness' self.failUnlessRaises(KeyError, self.metadata._get_exif_tag, key) def test_set_exif_tag_wrong(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Try to set a tag with wrong type tag = 'Not an exif tag' self.failUnlessRaises(TypeError, self.metadata._set_exif_tag, tag) self.assertEqual(self.metadata._tags['exif'], {}) def test_set_exif_tag_create(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Create a new tag tag = ExifTag('Exif.Thumbnail.Orientation', 1) self.assert_(tag.key not in self.metadata.exif_keys) self.metadata._set_exif_tag(tag.key, tag) self.assert_(tag.key in self.metadata.exif_keys) self.assertEqual(self.metadata._tags['exif'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._exifKeys()) self.assertEqual(self.metadata._image._getExifTag(tag.key)._getRawValue(), tag.raw_value) def test_set_exif_tag_overwrite(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Overwrite an existing tag tag = ExifTag('Exif.Image.DateTime', datetime.datetime(2009, 3, 20, 20, 32, 0)) self.metadata._set_exif_tag(tag.key, tag) self.assertEqual(self.metadata._tags['exif'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._exifKeys()) self.assertEqual(self.metadata._image._getExifTag(tag.key)._getRawValue(), tag.raw_value) def test_set_exif_tag_overwrite_already_cached(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Overwrite an existing tag already cached key = 'Exif.Image.Make' tag = self.metadata._get_exif_tag(key) self.assertEqual(self.metadata._tags['exif'][key], tag) new_tag = ExifTag(key, 'World Company') self.metadata._set_exif_tag(key, new_tag) self.assertEqual(self.metadata._tags['exif'], {key: new_tag}) self.assert_(key in self.metadata._image._exifKeys()) self.assertEqual(self.metadata._image._getExifTag(key)._getRawValue(), new_tag.raw_value) def test_set_exif_tag_direct_value_assignment(self): self.metadata.read() self.assertEqual(self.metadata._tags['exif'], {}) # Direct value assignment: pass a value instead of a fully-formed tag key = 'Exif.Thumbnail.Orientation' value = 1 self.metadata._set_exif_tag(key, value) self.assert_(key in self.metadata.exif_keys) self.assert_(key in self.metadata._image._exifKeys()) tag = self.metadata._get_exif_tag(key) self.assertEqual(tag.value, value) self.assertEqual(self.metadata._tags['exif'], {key: tag}) self.assertEqual(self.metadata._image._getExifTag(key)._getRawValue(), tag.raw_value) def test_delete_exif_tag_inexistent(self): self.metadata.read() key = 'Exif.Image.Artist' self.failUnlessRaises(KeyError, self.metadata._delete_exif_tag, key) def test_delete_exif_tag_not_cached(self): self.metadata.read() key = 'Exif.Image.DateTime' self.assertEqual(self.metadata._tags['exif'], {}) self.assert_(key in self.metadata.exif_keys) self.metadata._delete_exif_tag(key) self.assertEqual(self.metadata._tags['exif'], {}) self.failIf(key in self.metadata.exif_keys) def test_delete_exif_tag_cached(self): self.metadata.read() key = 'Exif.Image.DateTime' self.assert_(key in self.metadata.exif_keys) tag = self.metadata._get_exif_tag(key) self.assertEqual(self.metadata._tags['exif'][key], tag) self.metadata._delete_exif_tag(key) self.assertEqual(self.metadata._tags['exif'], {}) self.failIf(key in self.metadata.exif_keys) ########################### # Test IPTC-related methods ########################### def test_iptc_keys(self): self.metadata.read() self.assertEqual(self.metadata._keys['iptc'], None) keys = self.metadata.iptc_keys self.assertEqual(len(keys), 2) self.assertEqual(self.metadata._keys['iptc'], keys) def test_get_iptc_tag(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Get an existing tag key = 'Iptc.Application2.DateCreated' tag = self.metadata._get_iptc_tag(key) self.assert_(isinstance(tag, IptcTag)) self.assertEqual(self.metadata._tags['iptc'][key], tag) # Try to get an nonexistent tag key = 'Iptc.Application2.Copyright' self.failUnlessRaises(KeyError, self.metadata._get_iptc_tag, key) def test_set_iptc_tag_wrong(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Try to set a tag with wrong type tag = 'Not an iptc tag' self.failUnlessRaises(TypeError, self.metadata._set_iptc_tag, tag) self.assertEqual(self.metadata._tags['iptc'], {}) def test_set_iptc_tag_create(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Create a new tag tag = IptcTag('Iptc.Application2.Writer', ['Nobody']) self.assert_(tag.key not in self.metadata.iptc_keys) self.metadata._set_iptc_tag(tag.key, tag) self.assert_(tag.key in self.metadata.iptc_keys) self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._iptcKeys()) self.assertEqual(self.metadata._image._getIptcTag(tag.key)._getRawValues(), tag.raw_value) def test_set_iptc_tag_overwrite(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Overwrite an existing tag tag = IptcTag('Iptc.Application2.Caption', ['A picture.']) self.metadata._set_iptc_tag(tag.key, tag) self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._iptcKeys()) self.assertEqual(self.metadata._image._getIptcTag(tag.key)._getRawValues(), tag.raw_value) def test_set_iptc_tag_overwrite_already_cached(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Overwrite an existing tag already cached key = 'Iptc.Application2.Caption' tag = self.metadata._get_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'][key], tag) new_tag = IptcTag(key, ['A picture.']) self.metadata._set_iptc_tag(key, new_tag) self.assertEqual(self.metadata._tags['iptc'], {key: new_tag}) self.assert_(key in self.metadata._image._iptcKeys()) self.assertEqual(self.metadata._image._getIptcTag(key)._getRawValues(), new_tag.raw_value) def test_set_iptc_tag_direct_value_assignment(self): self.metadata.read() self.assertEqual(self.metadata._tags['iptc'], {}) # Direct value assignment: pass a value instead of a fully-formed tag key = 'Iptc.Application2.Writer' values = ['Nobody'] self.metadata._set_iptc_tag(key, values) self.assert_(key in self.metadata.iptc_keys) self.assert_(key in self.metadata._image._iptcKeys()) tag = self.metadata._get_iptc_tag(key) self.assertEqual(tag.value, values) self.assertEqual(self.metadata._tags['iptc'], {key: tag}) self.assertEqual(self.metadata._image._getIptcTag(key)._getRawValues(), tag.raw_value) def test_delete_iptc_tag_inexistent(self): self.metadata.read() key = 'Iptc.Application2.LocationCode' self.failUnlessRaises(KeyError, self.metadata._delete_iptc_tag, key) def test_delete_iptc_tag_not_cached(self): self.metadata.read() key = 'Iptc.Application2.Caption' self.assertEqual(self.metadata._tags['iptc'], {}) self.assert_(key in self.metadata.iptc_keys) self.metadata._delete_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'], {}) self.failIf(key in self.metadata.iptc_keys) def test_delete_iptc_tag_cached(self): self.metadata.read() key = 'Iptc.Application2.Caption' self.assert_(key in self.metadata.iptc_keys) tag = self.metadata._get_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'][key], tag) self.metadata._delete_iptc_tag(key) self.assertEqual(self.metadata._tags['iptc'], {}) self.failIf(key in self.metadata.iptc_keys) ########################## # Test XMP-related methods ########################## def test_xmp_keys(self): self.metadata.read() self.assertEqual(self.metadata._keys['xmp'], None) keys = self.metadata.xmp_keys self.assertEqual(len(keys), 2) self.assertEqual(self.metadata._keys['xmp'], keys) def test_get_xmp_tag(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Get an existing tag key = 'Xmp.dc.subject' tag = self.metadata._get_xmp_tag(key) self.assert_(isinstance(tag, XmpTag)) self.assertEqual(self.metadata._tags['xmp'][key], tag) # Try to get an nonexistent tag key = 'Xmp.xmp.Label' self.failUnlessRaises(KeyError, self.metadata._get_xmp_tag, key) def test_set_xmp_tag_wrong(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Try to set a tag with wrong type tag = 'Not an xmp tag' self.failUnlessRaises(TypeError, self.metadata._set_xmp_tag, tag) self.assertEqual(self.metadata._tags['xmp'], {}) def test_set_xmp_tag_create(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Create a new tag tag = XmpTag('Xmp.dc.title', {'x-default': 'This is not a title', 'fr-FR': "Ceci n'est pas un titre"}) self.assert_(tag.key not in self.metadata.xmp_keys) self.metadata._set_xmp_tag(tag.key, tag) self.assert_(tag.key in self.metadata.xmp_keys) self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._xmpKeys()) self.assertEqual(self.metadata._image._getXmpTag(tag.key)._getLangAltValue(), tag.raw_value) def test_set_xmp_tag_overwrite(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Overwrite an existing tag tag = XmpTag('Xmp.dc.format', ('image', 'png')) self.metadata._set_xmp_tag(tag.key, tag) self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag}) self.assert_(tag.key in self.metadata._image._xmpKeys()) self.assertEqual(self.metadata._image._getXmpTag(tag.key)._getTextValue(), tag.raw_value) def test_set_xmp_tag_overwrite_already_cached(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Overwrite an existing tag already cached key = 'Xmp.dc.subject' tag = self.metadata._get_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'][key], tag) new_tag = XmpTag(key, ['hello', 'world']) self.metadata._set_xmp_tag(key, new_tag) self.assertEqual(self.metadata._tags['xmp'], {key: new_tag}) self.assert_(key in self.metadata._image._xmpKeys()) self.assertEqual(self.metadata._image._getXmpTag(key)._getArrayValue(), new_tag.raw_value) def test_set_xmp_tag_direct_value_assignment(self): self.metadata.read() self.assertEqual(self.metadata._tags['xmp'], {}) # Direct value assignment: pass a value instead of a fully-formed tag key = 'Xmp.dc.title' value = {'x-default': 'This is not a title', 'fr-FR': "Ceci n'est pas un titre"} self.metadata._set_xmp_tag(key, value) self.assert_(key in self.metadata.xmp_keys) self.assert_(key in self.metadata._image._xmpKeys()) tag = self.metadata._get_xmp_tag(key) self.assertEqual(tag.value, value) self.assertEqual(self.metadata._tags['xmp'], {key: tag}) self.assertEqual(self.metadata._image._getXmpTag(key)._getLangAltValue(), tag.raw_value) def test_delete_xmp_tag_inexistent(self): self.metadata.read() key = 'Xmp.xmp.CreatorTool' self.failUnlessRaises(KeyError, self.metadata._delete_xmp_tag, key) def test_delete_xmp_tag_not_cached(self): self.metadata.read() key = 'Xmp.dc.subject' self.assertEqual(self.metadata._tags['xmp'], {}) self.assert_(key in self.metadata.xmp_keys) self.metadata._delete_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'], {}) self.failIf(key in self.metadata.xmp_keys) def test_delete_xmp_tag_cached(self): self.metadata.read() key = 'Xmp.dc.subject' self.assert_(key in self.metadata.xmp_keys) tag = self.metadata._get_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'][key], tag) self.metadata._delete_xmp_tag(key) self.assertEqual(self.metadata._tags['xmp'], {}) self.failIf(key in self.metadata.xmp_keys) ########################### # Test dictionary interface ########################### def test_getitem(self): self.metadata.read() # Get existing tags key = 'Exif.Image.DateTime' tag = self.metadata[key] self.assert_(isinstance(tag, ExifTag)) key = 'Iptc.Application2.Caption' tag = self.metadata[key] self.assert_(isinstance(tag, IptcTag)) key = 'Xmp.dc.format' tag = self.metadata[key] self.assert_(isinstance(tag, XmpTag)) # Try to get nonexistent tags keys = ('Exif.Image.SamplesPerPixel', 'Iptc.Application2.FixtureId', 'Xmp.xmp.Rating', 'Wrong.Noluck.Raise') for key in keys: self.failUnlessRaises(KeyError, self.metadata.__getitem__, key) def test_setitem(self): self.metadata.read() # Set new tags key = 'Exif.Photo.ExposureBiasValue' tag = ExifTag(key, make_fraction(0, 3)) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['exif']) self.failUnlessEqual(self.metadata._tags['exif'][key], tag) key = 'Iptc.Application2.City' tag = IptcTag(key, ['Barcelona']) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['iptc']) self.failUnlessEqual(self.metadata._tags['iptc'][key], tag) key = 'Xmp.dc.description' tag = XmpTag(key, {'x-default': 'Sunset picture.'}) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['xmp']) self.failUnlessEqual(self.metadata._tags['xmp'][key], tag) # Replace existing tags key = 'Exif.Photo.ExifVersion' tag = ExifTag(key, '0220') self.metadata[key] = tag self.failUnless(key in self.metadata._tags['exif']) self.failUnlessEqual(self.metadata._tags['exif'][key], tag) key = 'Iptc.Application2.Caption' tag = IptcTag(key, ['Sunset on Barcelona.']) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['iptc']) self.failUnlessEqual(self.metadata._tags['iptc'][key], tag) key = 'Xmp.dc.subject' tag = XmpTag(key, ['sunset', 'Barcelona', 'beautiful', 'beach']) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['xmp']) self.failUnlessEqual(self.metadata._tags['xmp'][key], tag) def test_delitem(self): self.metadata.read() # Delete existing tags key = 'Exif.Image.Make' del self.metadata[key] self.failIf(key in self.metadata._keys['exif']) self.failIf(key in self.metadata._tags['exif']) key = 'Iptc.Application2.Caption' del self.metadata[key] self.failIf(key in self.metadata._keys['iptc']) self.failIf(key in self.metadata._tags['iptc']) key = 'Xmp.dc.subject' del self.metadata[key] self.failIf(key in self.metadata._keys['xmp']) self.failIf(key in self.metadata._tags['xmp']) # Try to delete nonexistent tags keys = ('Exif.Image.SamplesPerPixel', 'Iptc.Application2.FixtureId', 'Xmp.xmp.Rating', 'Wrong.Noluck.Raise') for key in keys: self.failUnlessRaises(KeyError, self.metadata.__delitem__, key) def test_replace_tag_by_itself(self): # Test that replacing an existing tag by itself # doesn’t result in an ugly segmentation fault # (see https://bugs.launchpad.net/pyexiv2/+bug/622739). self.metadata.read() keys = self.metadata.exif_keys + \ self.metadata.iptc_keys + \ self.metadata.xmp_keys for key in keys: self.metadata[key] = self.metadata[key] ########################## # Test the image comment # ########################## def test_get_comment(self): self.metadata.read() self.failUnlessEqual(self.metadata.comment, 'Hello World!') def test_set_comment(self): self.metadata.read() comment = 'Welcome to the real world.' self.metadata.comment = comment self.failUnlessEqual(self.metadata.comment, comment) self.metadata.comment = None self.failUnlessEqual(self.metadata.comment, '') def test_delete_comment(self): self.metadata.read() del self.metadata.comment self.failUnlessEqual(self.metadata.comment, '') #################### # Test metadata copy #################### def _set_up_other(self): self.other = ImageMetadata.from_buffer(EMPTY_JPG_DATA) def test_copy_metadata(self): self.metadata.read() self._set_up_other() self.other.read() families = ('exif', 'iptc', 'xmp') for family in families: self.failUnlessEqual(getattr(self.other, '%s_keys' % family), []) self.metadata.copy(self.other) for family in ('exif', 'iptc', 'xmp'): self.failUnlessEqual(self.other._keys[family], None) self.failUnlessEqual(self.other._tags[family], {}) keys = getattr(self.metadata, '%s_keys' % family) self.failUnlessEqual(getattr(self.other._image, '_%sKeys' % family)(), keys) self.failUnlessEqual(getattr(self.other, '%s_keys' % family), keys) for key in self.metadata.exif_keys: self.failUnlessEqual(self.metadata[key].value, self.other[key].value) for key in self.metadata.iptc_keys: self.failUnlessEqual(self.metadata[key].value, self.other[key].value) for key in self.metadata.xmp_keys: self.failUnlessEqual(self.metadata[key].value, self.other[key].value) self.failUnlessEqual(self.metadata.comment, self.other.comment) ############################# # Test MutableMapping methods ############################# def _set_up_clean(self): self.clean = ImageMetadata.from_buffer(EMPTY_JPG_DATA) def test_mutablemapping(self): self._set_up_clean() self.clean.read() self.assertEqual(len(self.clean), 0) self.assertTrue('Exif.Image.DateTimeOriginal' not in self.clean) key = 'Exif.Image.DateTimeOriginal' correctDate = datetime.datetime(2007,03,11) incorrectDate = datetime.datetime(2009,03,25) tag_date = ExifTag(key,correctDate) false_tag_date = ExifTag(key,incorrectDate) self.clean[key] = tag_date self.assertEqual(len(self.clean), 1) self.assertTrue('Exif.Image.DateTimeOriginal' in self.clean) self.assertEqual(self.clean.get('Exif.Image.DateTimeOriginal', false_tag_date), tag_date) self.assertEqual(self.clean.get('Exif.Image.DateTime', tag_date), tag_date) key = 'Exif.Photo.UserComment' tag = ExifTag(key,'UserComment') self.clean[key] = tag key = 'Iptc.Application2.Caption' tag = IptcTag(key,['Caption']) self.clean[key] = tag key = 'Xmp.dc.subject' tag = XmpTag(key, ['subject', 'values']) self.clean[key] = tag self.assertTrue('Exif.Photo.UserComment' in self.clean) self.assertTrue('Iptc.Application2.Caption' in self.clean) self.assertTrue('Xmp.dc.subject' in self.clean) self.clean.clear() self.assertEqual(len(self.clean), 0) self.assertTrue('Exif.Photo.UserComment' not in self.clean) self.assertTrue('Iptc.Application2.Caption' not in self.clean) self.assertTrue('Xmp.dc.subject' not in self.clean) ########################### # Test the EXIF thumbnail # ########################### def _test_thumbnail_tags(self, there): keys = ('Exif.Thumbnail.Compression', 'Exif.Thumbnail.JPEGInterchangeFormat', 'Exif.Thumbnail.JPEGInterchangeFormatLength') for key in keys: self.assertEqual(key in self.metadata.exif_keys, there) def test_no_exif_thumbnail(self): self.metadata.read() thumb = self.metadata.exif_thumbnail self.assertEqual(thumb.mime_type, '') self.assertEqual(thumb.extension, '') self.assertEqual(thumb.data, '') self._test_thumbnail_tags(False) def test_set_exif_thumbnail_from_data(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA self.assertEqual(thumb.mime_type, 'image/jpeg') self.assertEqual(thumb.extension, '.jpg') self.assertEqual(thumb.data, EMPTY_JPG_DATA) self._test_thumbnail_tags(True) def test_set_exif_thumbnail_from_file(self): fd, pathname = tempfile.mkstemp(suffix='.jpg') os.write(fd, EMPTY_JPG_DATA) os.close(fd) self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.set_from_file(pathname) os.remove(pathname) self.assertEqual(thumb.mime_type, 'image/jpeg') self.assertEqual(thumb.extension, '.jpg') self.assertEqual(thumb.data, EMPTY_JPG_DATA) self._test_thumbnail_tags(True) def test_write_exif_thumbnail_to_file(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA fd, pathname = tempfile.mkstemp() os.close(fd) os.remove(pathname) thumb.write_to_file(pathname) pathname = pathname + thumb.extension fd = open(pathname, 'rb') self.assertEqual(fd.read(), EMPTY_JPG_DATA) fd.close() os.remove(pathname) def test_erase_exif_thumbnail(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA self.assertEqual(thumb.mime_type, 'image/jpeg') self.assertEqual(thumb.extension, '.jpg') self.assertEqual(thumb.data, EMPTY_JPG_DATA) self._test_thumbnail_tags(True) thumb.erase() self.assertEqual(thumb.mime_type, '') self.assertEqual(thumb.extension, '') self.assertEqual(thumb.data, '') self._test_thumbnail_tags(False) def test_set_exif_thumbnail_from_invalid_data(self): # No check on the format of the buffer is performed, therefore it will # always work. self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail thumb.data = 'invalid' self.assertEqual(thumb.mime_type, 'image/jpeg') self._test_thumbnail_tags(True) def test_set_exif_thumbnail_from_inexistent_file(self): self.metadata.read() self._test_thumbnail_tags(False) thumb = self.metadata.exif_thumbnail fd, pathname = tempfile.mkstemp() os.close(fd) os.remove(pathname) self.failUnlessRaises(IOError, thumb.set_from_file, pathname) self._test_thumbnail_tags(False) def test_exif_thumbnail_is_preview(self): self.metadata.read() self._test_thumbnail_tags(False) self.assertEqual(len(self.metadata.previews), 0) thumb = self.metadata.exif_thumbnail thumb.data = EMPTY_JPG_DATA self._test_thumbnail_tags(True) self.assertEqual(len(self.metadata.previews), 1) preview = self.metadata.previews[0] self.assertEqual(thumb.mime_type, preview.mime_type) self.assertEqual(thumb.extension, preview.extension) self.assertEqual(thumb.data, preview.data) ######################### # Test the IPTC charset # ######################### def test_guess_iptc_charset(self): # If no charset is defined, exiv2 guesses it from the encoding of the # metadata. self.metadata.read() self.assertEqual(self.metadata.iptc_charset, 'ascii') self.metadata['Iptc.Application2.City'] = [u'Córdoba'] self.assertEqual(self.metadata.iptc_charset, 'utf-8') def test_set_iptc_charset_utf8(self): self.metadata.read() self.assert_('Iptc.Envelope.CharacterSet' not in self.metadata.iptc_keys) self.assertEqual(self.metadata.iptc_charset, 'ascii') values = ('utf-8', 'utf8', 'u8', 'utf', 'utf_8') for value in values: self.metadata.iptc_charset = value self.assertEqual(self.metadata.iptc_charset, 'utf-8') self.metadata.iptc_charset = value.upper() self.assertEqual(self.metadata.iptc_charset, 'utf-8') def test_set_invalid_iptc_charset(self): self.metadata.read() self.assert_('Iptc.Envelope.CharacterSet' not in self.metadata.iptc_keys) values = ('invalid', 'utf-9', '3.14') for value in values: self.assertRaises(ValueError, self.metadata.__setattr__, 'iptc_charset', value) def test_set_unhandled_iptc_charset(self): # At the moment, the only charset handled is UTF-8. self.metadata.read() self.assert_('Iptc.Envelope.CharacterSet' not in self.metadata.iptc_keys) values = ('ascii', 'iso8859_15', 'shift_jis') for value in values: self.assertRaises(ValueError, self.metadata.__setattr__, 'iptc_charset', value) def test_delete_iptc_charset(self): self.metadata.read() key = 'Iptc.Envelope.CharacterSet' self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys) del self.metadata.iptc_charset self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys) self.metadata.iptc_charset = 'utf-8' self.assertEqual(self.metadata.iptc_charset, 'utf-8') self.assert_(key in self.metadata.iptc_keys) del self.metadata.iptc_charset self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys) self.metadata.iptc_charset = 'utf-8' self.assertEqual(self.metadata.iptc_charset, 'utf-8') self.assert_(key in self.metadata.iptc_keys) self.metadata.iptc_charset = None self.assertEqual(self.metadata.iptc_charset, 'ascii') self.assert_(key not in self.metadata.iptc_keys)