user ubuntu-de...@lists.ubuntu.com
usertag 614008 + ubuntu-patch natty
tags 614008 +patch
thanks


This patch is from Oliver Tilloy and his name is registered as the
author in the patch.  Any attribution should go to him.
Please close LP: #710874 in the changelog as well if you apply this or
something similar.

Author: Olivier Tilloy <oliv...@tilloy.net>
Description: Adapt all uses of pyexiv2 to the new API (≥ 0.2).
 This patch actually makes use of new APIs introduced in pyexiv2 0.3, and
 would therefore require minor changes to work with pyexiv2 0.2 too.
Bug: https://bugs.launchpad.net/phatch/+bug/585169

Index: phatch-natty/AUTHORS
===================================================================
--- phatch-natty.orig/AUTHORS	2011-01-27 21:12:16.609852151 +0100
+++ phatch-natty/AUTHORS	2011-01-27 21:12:23.659852151 +0100
@@ -27,7 +27,7 @@
 http://wxglade.sourceforge.net/ - wxGlade - Alberto Griggio
 pubsub.py - wxWidgets license - Oliver Schoenborn
 http://wiki.wxpython.org/TextCtrlAutoComplete - TextCtrlAutoComplete.py - wxWidgets license - Edward Flick (CDF Inc, http://www.cdf-imaging.com)
-http://tilloy.net/dev/pyexiv2/ - PyExiv2 - GPL license - Olivier Somon
+http://tilloy.net/dev/pyexiv2/ - pyexiv2 - GPL license - Olivier Tilloy
 http://www.gnome.org/projects/nautilus/ - python-nautilus - GPL license
 http://www.pythonware.com/products/pil/ - Python Image Library 1.1.6 - PIL license - Fredrik Lundh
 http://www.wxpython.org - wxPython 2.8.7.1 - wxWidgets license - Robin Dunn
Index: phatch-natty/phatch/core/pil.py
===================================================================
--- phatch-natty.orig/phatch/core/pil.py	2011-01-27 21:12:16.629852151 +0100
+++ phatch-natty/phatch/core/pil.py	2011-01-27 21:12:23.659852151 +0100
@@ -180,8 +180,8 @@
             metadata.InfoZexif: image}
         #check format -> readable/writable metadata with pyexiv2
         if exif and exif.is_readable_format(image.format):
-            self.pyexiv2 = pyexiv2.Image(path)
-            self.pyexiv2.readMetadata()
+            self.pyexiv2 = pyexiv2.ImageMetadata(path)
+            self.pyexiv2.read()
             self.writable_exif = exif.is_writable_format_exif(image.format)
             self.writable_iptc = exif.is_writable_format_exif(image.format)
             self.writable = self.writable_exif or self.writable_iptc
@@ -308,14 +308,9 @@
             self.assert_transparency()
             del self.get_pil().info[tag]
             return
-        pyexiv2_tag = self._fix(tag)  # pexiv2 demands str
-        # a bit clumsy but pyexiv2 does not support get or in
-        try:
-            pyexiv2_tag_value = self.pyexiv2[pyexiv2_tag]
-        except KeyError:
-            pyexiv2_tag_value = None
-        if self.pyexiv2 and pyexiv2_tag_value != None:
-            self.pyexiv2[pyexiv2_tag] = None
+        pyexiv2_tag = self._fix(tag)  # pyexiv2 demands str
+        if pyexiv2_tag in self.pyexiv2:
+            del self.pyexiv2[pyexiv2_tag]
         if tag in self:
             super(InfoPhoto, self).__delitem__(tag)
 
@@ -343,7 +338,26 @@
             super(InfoPhoto, self).__setitem__(tag, value)
         if metadata.RE_PYEXIV2_TAG_EDITABLE.match(tag):
             try:
-                self.pyexiv2[self._fix(tag)] = value
+                key = self._fix(tag)
+                if isinstance(value, metadata.DateTime):
+                    self.pyexiv2[key] = value.datetime
+                else:
+                    if key.startswith('Iptc'):
+                        stripped = value.strip()
+                        if stripped.startswith('[') and stripped.endswith(']'):
+                            # This looks like a list of values
+                            try:
+                                values = eval(stripped)
+                            except SyntaxError:
+                                value = [value]
+                            else:
+                                if isinstance(values, list):
+                                   value = values
+                                else:
+                                   value = [value]
+                        else:
+                            value = [value]
+                    self.pyexiv2[key] = value
             except Exception, message:
                 raise KeyError('%s:\n%s'
                     % (_('Impossible to write tag "%s"') % tag, message))
Index: phatch-natty/phatch/data/info.py
===================================================================
--- phatch-natty.orig/phatch/data/info.py	2011-01-27 21:12:16.639852151 +0100
+++ phatch-natty/phatch/data/info.py	2011-01-27 21:12:23.659852151 +0100
@@ -310,9 +310,9 @@
             'license': 'wxWidgets license',
             'url': 'http://wiki.wxpython.org/TextCtrlAutoComplete',
         },
-        {'name': 'PyExiv2',
+        {'name': 'pyexiv2',
             'url': 'http://tilloy.net/dev/pyexiv2/',
-            'author': 'Olivier Somon',
+            'author': 'Olivier Tilloy',
             'license': 'GPL license',
         },
         {'name': 'python-nautilus',
Index: phatch-natty/phatch/lib/_pyexiv2.py
===================================================================
--- phatch-natty.orig/phatch/lib/_pyexiv2.py	2011-01-27 21:12:16.659852151 +0100
+++ phatch-natty/phatch/lib/_pyexiv2.py	2011-01-27 21:12:23.659852151 +0100
@@ -15,15 +15,13 @@
 
 # Follows PEP8
 
-import re
+import os
+import shutil
+import system
+
 import pyexiv2
 
-BROKEN = 'Exif[.]Canon'
-RE_BROKEN = re.compile(BROKEN)
-ISSUES = 'Saving metadata to %s caused following issues:\n'
-FAILED = '''
-Failed to save metadata to %s:\npyexiv2: %s
-Trying again by ignoring tags with following pattern:\n%s\n'''
+FAILED = 'Failed to save metadata to %s:\npyexiv2: %s\n'
 
 #info taken from http://dev.exiv2.org/wiki/exiv2/Supported_image_formats
 READ_EXIF = ['JPEG', 'EXV', 'CR2', 'CRW', 'MRW', 'TIFF', 'DNG', 'NEF', 'PEF',
@@ -76,7 +74,7 @@
         target_format=None, thumbdata=None):
     """
     :param source_pyexiv2_image: file opened by pyexiv2
-    :type source_pyexiv2_image: pyexiv2.Image
+    :type source_pyexiv2_image: pyexiv2.ImageMetadata
     :param target: target filename
     :type target: string
     :param source_format: source format e.g. obtained by PIL
@@ -89,106 +87,21 @@
     #if there is nothing to read or write, return immediately
     if not is_writable_format(target_format):
         return ''
-    #correct tags
     if not source_pyexiv2_image:
         return ''
 
-    #make two attempts to copy metadata:
-    #1. normal
-    #2. exclude tags which (might) break exiv2 (eg Canon tuples)
-
-    # This will probably be obsolete for python-pyexiv2 0.2
-    # -> If that is True add a version check
-
-    #verify if there are tags which might break exiv2
-    broken_tag = None
-
-    for tag in list(source_pyexiv2_image.exifKeys()) + \
-        list(source_pyexiv2_image.iptcKeys()):
-        if RE_BROKEN.match(tag):
-            broken_tag = RE_BROKEN
-            break
-
-    #copy the tags
-    log = ''
-
-    #attempt to copy metadata
+    target, temp = read_metadata_temp(target)
     try:
-        warnings = _copy_metadata(source_pyexiv2_image, target,
-            source_format, target_format, broken_tag, thumbdata)
-        copied = True
-    except Exception, message:
-        copied = False
-
-    #if metadata copied succesfully, check for warnings
-    if copied:
-        if warnings:
-            log += ISSUES % target + warnings
+        source_pyexiv2_image.copy(target)
+    except Exception, error:
+        if temp:
+            temp.close()
+        return FAILED % (target.filename, error.message)
     else:
-        log = FAILED % (target, message, BROKEN)
-
-    return log
-
-
-def _copy_metadata(source_pyexiv2_image, target, source_format=None,
-        target_format=None, broken_tag=None, thumbdata=None):
-    """
-    :param source_pyexiv2_image: file opened by pyexiv2
-    :type source_pyexiv2_image: pyexiv2.Image
-    :param target: target filename
-    :type target: string
-    :param source_format: source format e.g. obtained by PIL
-    :type source_format: string
-    :param target_format: target format e.g. obtained by PIL
-    :type target_format: string
-    :param broken_tag: tag which might possibly break the metadata writing
-    :type broken_tag: compiled regular expression
-    :param thumbdata: new thumbnail (e.g. with StringIO, see :mod:`imtools`)
-    :type thumbdata: string
-    """
-    #read target
-    target = pyexiv2.Image(target)
-    target.readMetadata()
-    warnings = []
-    written = False
-    #copy exif metadata
-    if (not source_format or source_format in READ_EXIF) and \
-        (not target_format or target_format in WRITE_EXIF):
-        for tag in source_pyexiv2_image.exifKeys():
-            if not(broken_tag and broken_tag.match(tag)):
-                try:
-                    #the following is more or less the same as
-                    #target[tag] = source_pyexiv2_image[tag]
-                    #but prevents conversions
-                    target._Image__setExifTag(tag,
-                        source_pyexiv2_image._Image__getExifTag(tag)[1])
-                    written = True
-                except Exception, message:
-                    message = '%s: %s' % (tag, message)
-                    warnings.append(message)
-    #copy iptc metadata
-    if (not source_format or source_format in READ_IPTC) and \
-        (not target_format or target_format in WRITE_IPTC):
-        for tag in source_pyexiv2_image.iptcKeys():
-            try:
-                target[tag] = source_pyexiv2_image[tag]
-                written = True
-            except Exception, message:
-                message = '%s: %s' % (tag, message)
-                warnings.append(message)
-    #copy comment
-    if (not source_format or source_format in READ_COMMENT) and \
-        (not target_format or target_format in WRITE_COMMENT):
-        try:
-            target.setComment(source_pyexiv2_image.getComment())
-            written = True
-        except Exception, message:
-            warnings.append(message)
-    warnings.append(write_thumbdata(target, thumbdata))
-    #save metadata (this might rise an exception)
-    if written:
-        target.writeMetadata()
-    return '\n'.join(warnings)
+        target.write()
+        if temp:
+            temp.close(dest=target.filename)
+        return ''
 
 
 def extension_to_image_format(ext):
@@ -202,7 +115,7 @@
 
 def read_thumbdata(image):
     try:
-        return image.getThumbnailData()
+        return image.exif_thumbnail.data
     except Exception, message:
         return None
 
@@ -211,7 +124,7 @@
     if (thumbdata is None):
         return ''
     try:
-        image.setThumbnailData(thumbdata)
+        image.exif_thumbnail.data = thumbdata
         return ''
     except Exception, message:
         return unicode(message)
@@ -234,7 +147,21 @@
 def flush(image, thumbdata):
     warnings = [write_thumbdata(image, thumbdata)]
     try:
-        image.writeMetadata()
+        image.write()
     except Exception, message:
         warnings.append(unicode(message))
     return '\n'.join(warnings)
+
+
+def read_metadata_temp(filename):
+    try:
+        image = pyexiv2.ImageMetadata(filename)
+        image.read()
+        temp = None
+    except (IOError, UnicodeDecodeError):
+        temp = system.TempFile(suffix=os.path.splitext(filename)[-1])
+        shutil.copy2(filename, temp.path)
+        image = pyexiv2.ImageMetadata(temp.path)
+        image.read()
+    return image, temp
+
Index: phatch-natty/phatch/lib/imageTable.py
===================================================================
--- phatch-natty.orig/phatch/lib/imageTable.py	2011-01-27 21:12:16.669852151 +0100
+++ phatch-natty/phatch/lib/imageTable.py	2011-01-27 21:12:23.669852151 +0100
@@ -27,6 +27,7 @@
 import glob
 import os
 import re
+import bisect
 
 import metadata
 import openImage
@@ -204,8 +205,8 @@
     #---key
     def _add_key(self, key):
         if not(key in self.keys):
-            self.keys = self.keys[:self.key_amount] + [key]\
-                                + self.keys[self.key_amount:]
+            # keep the list of keys alphabetically sorted
+            bisect.insort(self.keys, key, hi=self.key_amount)
             self.key_amount += 1
 
     def add_key(self, key, value=''):
@@ -306,11 +307,28 @@
         key, image = self._get_key_image(row, col)
         if value == self.get_cell_value(row, col):
             return
+        if value == '':
+            value = None
         return self.set_image_key_value(image, key, value)
 
     def set_image_key_value(self, image, key, value):
         if not(value is None):
             value = unicode(value)
+            if key.startswith('Iptc'):
+                stripped = value.strip()
+                if stripped.startswith('[') and stripped.endswith(']'):
+                    # This looks like a list of values
+                    try:
+                        values = eval(stripped)
+                    except SyntaxError:
+                        value = [value]
+                    else:
+                        if isinstance(values, list):
+                           value = values
+                        else:
+                           value = [value]
+                else:
+                    value = [value]
         return self._write(
             changes=((image, {key: value}), ),
             error_message=_('Unable to save tag <%s>'))
@@ -353,18 +371,19 @@
         """
         log = []
         keys_to_delete = set()
+        keys_to_add = set()
         for image, image_changes in changes:
             # try to save to image file
             try:
-                exiv2_image = pyexiv2.Image(image.filename)
-                exiv2_image.readMetadata()
+                exiv2_image = pyexiv2.ImageMetadata(image.filename)
+                exiv2_image.read()
                 for key, value in image_changes.items():
                     exiv2_key = str(key.replace(SEPARATOR, '.'))
                     if value:
                         exiv2_image[exiv2_key] = value
                     else:
                         del exiv2_image[exiv2_key]
-                exiv2_image.writeMetadata()
+                exiv2_image.write()
             except Exception, error:
                 log.append('%s:\n%s'\
                     % (error_message % key, unicode(error)))
@@ -373,14 +392,28 @@
             image.update_time()
             if value:
                 image.info[key] = value
+                before = set(image.info.keys())
                 metadata.InfoExtract.expand_var(image.info, key,
                     metadata.convert_from_string(value))
+                after = set(image.info.keys())
+                removed = before - after
+                for key in removed:
+                    keys_to_delete.add(key)
+                added = after - before
+                for key in added:
+                    keys_to_add.add(key)
             else:
+                for k in image.info.keys():
+                    if k.startswith('%s[' % key):
+                        del image.info[k]
+                        keys_to_delete.add(k)
                 del image.info[key]
                 keys_to_delete.add(key)
         for key in keys_to_delete:
             if self.is_key_empty(key):
                 self._delete_key(key)
+        for key in keys_to_add:
+            self._add_key(key)
         return '\n'.join(log)
 
     #---selecting
Index: phatch-natty/phatch/lib/metadata.py
===================================================================
--- phatch-natty.orig/phatch/lib/metadata.py	2011-01-27 21:12:16.689852151 +0100
+++ phatch-natty/phatch/lib/metadata.py	2011-01-27 21:12:23.669852151 +0100
@@ -899,7 +899,7 @@
         :type var: string
         """
         return self.convert(self._source['%s.%s' \
-            % (self.type, var.replace('_', '.'))])
+            % (self.type, var.replace('_', '.'))].value)
 
     def _extract_others_from_keys(self, exif_keys):
         """Extract all other vars"""
@@ -907,7 +907,7 @@
             _var = var.replace('.', '_')
             if not(_var in self.dict):
                 try:
-                    value = self._source[var]
+                    value = self._source[var].value
                 except:
                     continue
                 self.dict[_var] = self.convert(value)
@@ -921,21 +921,16 @@
         cls._pyexiv2 = _pyexiv2
 
     @classmethod
-    def _get_source_from_file(cls, filename, all=False):
+    def _get_source_from_file(cls, filename):
         """Load the pyexiv2 source from a file.
 
         :param filename: filename of the source file
         :type filename: string
-        :param all: cache all tags
-        :type all: bool
         """
         format = imtools.get_format(os.path.splitext(filename)[-1][1:])
         if cls._pyexiv2.is_readable_format(format):
-            source = cls.pyexiv2.Image(filename)
-            source.readMetadata()
-            if all:
-                source.cacheAllExifTags()
-                source.cacheAllIptcTags()
+            source = cls.pyexiv2.ImageMetadata(filename)
+            source.read()
             return source
         else:
             return {}
@@ -949,8 +944,8 @@
     >>> info['Exif_Image_DateTime']
     DateTime('2010:03:03 11:03:08')
     >>> import pyexiv2
-    >>> exif = pyexiv2.Image(filename)
-    >>> exif.readMetadata()
+    >>> exif = pyexiv2.ImageMetadata(filename)
+    >>> exif.read()
     >>> info = InfoExif(exif)
     >>> info['Exif_Image_DateTime']
     DateTime('2010:03:03 11:03:08')
@@ -1124,13 +1119,13 @@
         """Extract orientation from source image as integer."""
         try:
             #be careful to use dots here instead of _
-            self.dict['orientation'] = self._source['Exif.Image.Orientation']
+            self.dict['orientation'] = self._source['Exif.Image.Orientation'].value
         except KeyError:
             self.dict['orientation'] = 1
 
     def _extract_others(self):
         """Extract all other vars"""
-        self._extract_others_from_keys(self._source.exifKeys())
+        self._extract_others_from_keys(self._source.exif_keys)
 
     regex = re.compile('^Exif|^orientation$')
     type = 'Exif'
@@ -1150,8 +1145,8 @@
     >>> info['Iptc_Application2_RecordVersion']
     0
     >>> import pyexiv2
-    >>> exif = pyexiv2.Image(filename)
-    >>> exif.readMetadata()
+    >>> exif = pyexiv2.ImageMetadata(filename)
+    >>> exif.read()
     >>> info = InfoIptc(exif)
     >>> info['Iptc_Application2_RecordVersion']
     0
@@ -1170,7 +1165,7 @@
 
     def _extract_others(self):
         """Extract all other vars"""
-        self._extract_others_from_keys(self._source.iptcKeys())
+        self._extract_others_from_keys(self._source.iptc_keys)
 
     type = 'Iptc'
     regex = re.compile('^Iptc_')
@@ -2129,9 +2124,22 @@
         if isinstance(value, DateTime):
             for attr in DateTime.attrs:
                 d['%s.%s' % (key, attr)] = getattr(value, attr)
-        elif type(value) in (tuple, list) and len(value) < 10:
-            for index, value_index in enumerate(value):
-                d['%s[%d]' % (key, index)] = value[index]
+        elif isinstance(value, list) and len(value) < 10:
+            # IPTC values (always a list)
+            if len(value) == 1:
+                d[key] = value[0]
+            else:
+                for index, value_index in enumerate(value):
+                    d['%s[%d]' % (key, index)] = value[index]
+                # Delete extra values if needed:
+                index = len(value)
+                while True:
+                    try:
+                        del d['%s[%d]' % (key, index)]
+                    except KeyError:
+                        break
+                    else:
+                        index = index + 1
         elif hasattr(value, 'denominator') and value.denominator != 1:
             d['%s.denominator' % key] = value.denominator
             d['%s.numerator' % key] = value.numerator
Index: phatch-natty/phatch/lib/openImage.py
===================================================================
--- phatch-natty.orig/phatch/lib/openImage.py	2011-01-27 21:12:16.709852151 +0100
+++ phatch-natty/phatch/lib/openImage.py	2011-01-27 21:12:23.669852151 +0100
@@ -77,9 +77,9 @@
 def open_image_exif_thumb(uri):
     if pyexiv2:
         try:
-            pyexiv2_image = pyexiv2.Image(uri)
-            pyexiv2_image.readMetadata()
-            thumb_data = pyexiv2_image.getThumbnailData()
+            pyexiv2_image = pyexiv2.ImageMetadata(uri)
+            pyexiv2_image.read()
+            thumb_data = pyexiv2_image.exif_thumbnail.data
             if thumb_data:
                 return imtools.open_image_data(thumb_data)
         except Exception, details:
Index: phatch-natty/tests/test_suite/utils.py
===================================================================
--- phatch-natty.orig/tests/test_suite/utils.py	2011-01-27 21:12:16.729852151 +0100
+++ phatch-natty/tests/test_suite/utils.py	2011-01-27 21:12:23.669852151 +0100
@@ -168,12 +168,12 @@
     """Compare images metadata"""
     try:
         import pyexiv2
-        image1 = pyexiv2.Image(image1_path)
-        image1.readMetadata()
-        image2 = pyexiv2.Image(image2_path)
-        image2.readMetadata()
-        metadata1 = sorted([(key, image1[key]) for key in image1.exifKeys()])
-        metadata2 = sorted([(key, image2[key]) for key in image2.exifKeys()])
+        image1 = pyexiv2.ImageMetadata(image1_path)
+        image1.read()
+        image2 = pyexiv2.ImageMetadata(image2_path)
+        image2.read()
+        metadata1 = sorted([(key, image1[key].value) for key in image1.exif_keys])
+        metadata2 = sorted([(key, image2[key].value) for key in image2.exif_keys])
         return metadata1 == metadata2
     except IOError:
         return True

Reply via email to