Exemple #1
0
    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])
Exemple #2
0
 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)
Exemple #3
0
 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)
Exemple #5
0
    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)
Exemple #6
0
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
Exemple #7
0
 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)
Exemple #8
0
    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)
Exemple #9
0
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
Exemple #10
0
 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)
Exemple #11
0
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
Exemple #12
0
def test_not_read_raises(metadata_ro, scratch_directory, suite):
    # http://bugs.launchpad.net/pyexiv2/+bug/687373

    locals = {
        'metadata': metadata_ro.metadata,
    }
    if 'other' in suite:
        other = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
        other.read()
        locals['other'] = other

    if 'nonexistent_jpg' in suite:
        # tempfile.mktemp would actually be safe here because we are
        # the only process that can write to the scratch_directory,
        # but then we'd have to suppress warnings.  This can be
        # cleaned up if and when thumb.write_to_file grows the ability
        # to write to a filelike.
        fd, pathname = tempfile.mkstemp(dir=scratch_directory, suffix='.jpg')
        os.close(fd)
        os.remove(pathname)
        locals['nonexistent_jpg'] = pathname

    if 'empty_jpg' in suite:
        with tempfile.NamedTemporaryFile(dir=scratch_directory,
                                         suffix='.jpg',
                                         delete=False) as fp:
            fp.write(EMPTY_JPG_DATA)
            locals['empty_jpg'] = fp.name

    with pytest.raises(OSError):
        exec(suite, {}, locals)
Exemple #13
0
def test_copy_metadata(metadata_ro):
    m = metadata_ro.metadata
    m.read()
    other = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
    other.read()
    families = ('exif', 'iptc', 'xmp')

    for family in families:
        assert getattr(other, '%s_keys' % family) == []

    m.copy(other)

    for family in ('exif', 'iptc', 'xmp'):
        assert other._keys[family] is None
        assert other._tags[family] == {}
        keys = getattr(m, '%s_keys' % family)
        assert getattr(other._image, '_%sKeys' % family)() == keys
        assert getattr(other, '%s_keys' % family) == keys

    for key in m.exif_keys:
        assert m[key].value == other[key].value

    for key in m.iptc_keys:
        assert m[key].value == other[key].value

    for key in m.xmp_keys:
        assert m[key].value == other[key].value

    assert m.comment == other.comment
Exemple #14
0
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 }
Exemple #15
0
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
Exemple #16
0
    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 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
Exemple #18
0
def test_from_modified_buffer(smiley):
    m1 = metadata_from_buffer(smiley)
    m1.read()
    key = 'Exif.Image.ImageDescription'
    value = 'my kingdom for a semiquaver'
    m1[key] = value
    m1.write()

    m2 = ImageMetadata.from_buffer(m1.buffer)
    m2.read()
    assert m2[key].value == value
Exemple #19
0
    def test_from_modified_buffer(self):
        m1 = self._metadata_from_buffer()
        m1.read()
        key = "Exif.Image.ImageDescription"
        value = "my kingdom for a semiquaver"
        m1[key] = value
        m1.write()

        m2 = ImageMetadata.from_buffer(m1.buffer)
        m2.read()
        self.assertEqual(m2[key].value, value)
 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
Exemple #21
0
    def test_from_modified_buffer(self):
        m1 = self._metadata_from_buffer()
        m1.read()
        key = 'Exif.Image.ImageDescription'
        value = 'my kingdom for a semiquaver'
        m1[key] = value
        m1.write()

        m2 = ImageMetadata.from_buffer(m1.buffer)
        m2.read()
        self.assertEqual(m2[key].value, value)
Exemple #22
0
 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)
Exemple #23
0
def metadata_rw(jpg_with_tags, scratch_directory):
    """A fresh *writable* ImageMetadata object for a *copy* of the
       jpg_with_tags.  Returns a 2-tuple (pathname, ImageMetadata).
       read() has not yet been called.
    """
    (nfd, nname) = tempfile.mkstemp(suffix='.jpg', dir=scratch_directory)
    # create ofp first so that nfd is closed even if the second open throws
    with open(nfd, "wb") as ofp, open(jpg_with_tags, "rb") as ifp:
        shutil.copyfileobj(ifp, ofp)
    # nfd is now closed
    os.chmod(nname, 0o0600)  # rw-------
    return MetadataWithPath(nname, ImageMetadata(nname))
Exemple #24
0
    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])
Exemple #25
0
 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
Exemple #26
0
 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)
Exemple #27
0
 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)
Exemple #28
0
 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)
     self.assertRaises(IOError, getattr, self.metadata, 'buffer')
     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, thumb, 'data')
     self.assertRaises(IOError, setattr, thumb, 'data', EMPTY_JPG_DATA)
     self.assertRaises(IOError, getattr, self.metadata, 'iptc_charset')
Exemple #29
0
def test_mutablemapping():
    clean = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
    clean.read()

    assert len(clean) == 0
    assert 'Exif.Image.DateTimeOriginal' not in 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)
    clean[key] = tag_date

    assert len(clean) == 1
    assert 'Exif.Image.DateTimeOriginal' in clean
    assert clean.get('Exif.Image.DateTimeOriginal', false_tag_date) == tag_date
    assert clean.get('Exif.Image.DateTime', tag_date) == tag_date

    key = 'Exif.Photo.UserComment'
    tag = ExifTag(key, 'UserComment')
    clean[key] = tag
    key = 'Iptc.Application2.Caption'
    tag = IptcTag(key, ['Caption'])
    clean[key] = tag
    key = 'Xmp.dc.subject'
    tag = XmpTag(key, ['subject', 'values'])
    clean[key] = tag

    assert 'Exif.Photo.UserComment' in clean
    assert 'Iptc.Application2.Caption' in clean
    assert 'Xmp.dc.subject' in clean

    clean.clear()
    assert len(clean) == 0

    assert 'Exif.Photo.UserComment' not in clean
    assert 'Iptc.Application2.Caption' not in clean
    assert 'Xmp.dc.subject' not in clean
Exemple #30
0
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
Exemple #31
0
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()
Exemple #32
0
 def _set_up_clean(self):
     self.clean = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
Exemple #33
0
 def test_from_original_buffer(self):
     m1 = self._metadata_from_buffer()
     m2 = ImageMetadata.from_buffer(m1.buffer)
     self.assertEqual(hashlib.md5(m2.buffer).hexdigest(), self.md5sum)
Exemple #34
0
 def _metadata_from_buffer(self):
     fd = open(self.filepath, "rb")
     data = fd.read()
     fd.close()
     return ImageMetadata.from_buffer(data)
Exemple #35
0
 def _set_up_clean(self):
     self.clean = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
Exemple #36
0
def metadata_ro(jpg_with_tags):
    """A fresh *read-only* ImageMetadata object for the jpg_with_tags.
       Returns a 2-tuple (pathname, ImageMetadata).
       read() has not yet been called.  write() will fail.
    """
    return MetadataWithPath(jpg_with_tags, ImageMetadata(jpg_with_tags))
Exemple #37
0
 def _test_filename(self, filename):
     self._create_file(filename)
     m = ImageMetadata(filename)
     m.read()
     os.remove(filename)
Exemple #38
0
 def setUp(self):
     self.metadata = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
     self.metadata.read()
Exemple #39
0
 def setUp(self):
     self.metadata = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
     self.metadata.read()
Exemple #40
0
 def _set_up_other(self):
     self.other = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
Exemple #41
0
 def _set_up_other(self):
     self.other = ImageMetadata.from_buffer(EMPTY_JPG_DATA)
Exemple #42
0
#
# 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.

Exemple #43
0
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)
Exemple #44
0
 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
Exemple #45
0
 def test_read_nonexistent_file(self):
     metadata = ImageMetadata('idontexist')
     self.failUnlessRaises(IOError, metadata.read)
Exemple #46
0
def get_metadata():
    data = ImageMetadata(COPY)
    data.read()
    return data
Exemple #47
0
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)