From bfa368efcc7589c478703a908fa7eae3fd079f65 Mon Sep 17 00:00:00 2001 From: Jim Easterbrook Date: Mon, 22 May 2023 15:00:53 +0100 Subject: [PATCH 1/3] Handle NULL characters in Exif comment strings The Exif standard says NULLs aren't needed and spaces should be used for padding, but some files have NULL. Python versions < 3.10 don't object to NULLs in the decoded string. --- src/photini/__init__.py | 2 +- src/photini/exiv2.py | 38 +++++++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/photini/__init__.py b/src/photini/__init__.py index 12494106..410102a2 100644 --- a/src/photini/__init__.py +++ b/src/photini/__init__.py @@ -1,4 +1,4 @@ """Full documentation is at https://photini.readthedocs.io/""" __version__ = '2023.5.1' -build = '2719 (e80171b)' +build = '2720 (339bf72)' diff --git a/src/photini/exiv2.py b/src/photini/exiv2.py index 4efd7cc7..7c04bd12 100644 --- a/src/photini/exiv2.py +++ b/src/photini/exiv2.py @@ -335,25 +335,33 @@ def get_exif_comment(self, tag, datum): if charset != exiv2.CharsetId.undefined: logger.warning('%s: %s: unknown charset', self._name, tag) raw_value = data + result = None for encoding in encodings: result = self.decode_string(tag, raw_value, encoding) if result: - return result - detector = chardet.universaldetector.UniversalDetector() - for i in range(0, len(raw_value), 100): - detector.feed(raw_value[i:i+100]) - if detector.done: break - detector.close() - encoding = detector.result['encoding'] - if encoding: - result = self.decode_string(tag, raw_value, encoding) - if result: - return result - logger.error( - '%s: %s: %d bytes binary data will be deleted when metadata' - ' is saved', self._name, tag, value.size()) - return None + else: + detector = chardet.universaldetector.UniversalDetector() + for i in range(0, len(raw_value), 100): + detector.feed(raw_value[i:i+100]) + if detector.done: + break + detector.close() + encoding = detector.result['encoding'] + if encoding: + result = self.decode_string(tag, raw_value, encoding) + if result and '\0' in result: + # terminating NULLs are allowed + result = result.strip('\0') + if '\0' in result: + # NULLs within the string are not allowed + result = None + if not result: + logger.error( + '%s: %s: %d bytes binary data will be deleted when metadata' + ' is saved', self._name, tag, value.size()) + return None + return result def get_exif_value(self, tag): datum = self._exifData.findKey(exiv2.ExifKey(tag)) From 64e922285306db952f429155a133c5bb74a4be97 Mon Sep 17 00:00:00 2001 From: Jim Easterbrook Date: Mon, 22 May 2023 15:26:04 +0100 Subject: [PATCH 2/3] Better handling of empty XMP structured data --- src/photini/__init__.py | 2 +- src/photini/types.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/photini/__init__.py b/src/photini/__init__.py index 410102a2..fb7f892f 100644 --- a/src/photini/__init__.py +++ b/src/photini/__init__.py @@ -1,4 +1,4 @@ """Full documentation is at https://photini.readthedocs.io/""" __version__ = '2023.5.1' -build = '2720 (339bf72)' +build = '2721 (bfa368e)' diff --git a/src/photini/types.py b/src/photini/types.py index 7f3b1269..17f2615a 100644 --- a/src/photini/types.py +++ b/src/photini/types.py @@ -690,10 +690,10 @@ def get_type(cls, key, value): @classmethod def from_exiv2(cls, file_value, tag): + file_value = file_value or {} if isinstance(file_value, (list, tuple)): # "legacy" list of string values file_value = dict(zip(cls.legacy_keys, file_value)) - file_value = file_value or {} for key, value in file_value.items(): file_value[key] = cls.get_type(key, value).from_exiv2(value, tag) return cls(file_value) From c1be3779775d70a7abb60d2f661f642986f4f064 Mon Sep 17 00:00:00 2001 From: Jim Easterbrook Date: Mon, 22 May 2023 15:32:45 +0100 Subject: [PATCH 3/3] Update change log --- CHANGELOG.txt | 6 ++++++ src/photini/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 71784a80..925bdc81 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -16,6 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . +Changes in v2023.5.1: + 1/ Tab labels use two lines instead of eliding to fit width. + 2/ Ignore NULL bytes in some phone images' Exif comment values. + 3/ Italian localisation is complete. + 4/ Other minor improvements and bug fixes. + Changes in v2023.5.0: 1/ Latitude & longitude values no longer have comma separator. 2/ All numeric values are localised, e.g. to use decimal comma. diff --git a/src/photini/__init__.py b/src/photini/__init__.py index fb7f892f..f8717376 100644 --- a/src/photini/__init__.py +++ b/src/photini/__init__.py @@ -1,4 +1,4 @@ """Full documentation is at https://photini.readthedocs.io/""" __version__ = '2023.5.1' -build = '2721 (bfa368e)' +build = '2722 (64e9222)'