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