Skip to content

Commit 256872b

Browse files
author
James Campbell
committed
updates to fix 40, 24, 32, 26, 35, 42, 15, 38, 39, 41
1 parent dfbfd90 commit 256872b

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
## 2.2.0 (2025-10-02)
2+
3+
### Bug Fixes
4+
- **Issue #40**: Clarified license statements - now consistently states "Artistic-1.0 OR GPL-1.0-or-later"
5+
- **Issue #24**: Changed "Marker scan hit start of image data" to INFO level when `force=True` is used
6+
- **Issue #32**: Fixed charset recognition for ISO 2022 escape sequences (UTF-8 as `\x1b%G`)
7+
- **Issue #26**: Added validation for float/NaN values in `packedIIMData()` to prevent TypeError
8+
9+
### New Features
10+
- **Issue #35**: Added 'credit line' field support per IPTC Core 1.1 (backward compatible with 'credit')
11+
- **Issue #42**: Added 'destination' field as alias for 'original transmission reference'
12+
13+
### Improvements
14+
- **Issue #15**: Enhanced IPTC tag collection with better field mappings
15+
- **Issue #38**: Verified backup file behavior (use `options={'overwrite': True}` to avoid ~ files)
16+
- Better error handling and logging throughout
17+
18+
### Notes
19+
- **Issue #39, #41**: Ready for PyPI release with all fixes from master branch
20+
21+
---
22+
123
Updating builds to target 3.9.7
224

325
2.1: Fixes merged to save modified IPTC info images

iptcinfo3.py

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
# All rights reserved.
1212
#
1313
# This program is free software; you can redistribute it and/or modify
14-
# it under the same terms as Python itself.
14+
# it under the terms of the Artistic License or the GNU General Public
15+
# License (GPL). You may choose either license.
1516
#
1617
# VERSION = '1.9';
1718
"""
@@ -26,7 +27,7 @@
2627
from struct import pack, unpack
2728
import json
2829

29-
__version__ = '2.1.4'
30+
__version__ = '2.2.0'
3031
__author__ = 'Gulácsi, Tamás'
3132
__updated_by__ = 'Campbell, James'
3233

@@ -481,7 +482,7 @@ def collect_adobe_parts(data):
481482
101: 'country/primary location name',
482483
103: 'original transmission reference',
483484
105: 'headline',
484-
110: 'credit',
485+
110: 'credit line', # Updated from 'credit' to 'credit line' per IPTC Core 1.1
485486
115: 'source',
486487
116: 'copyright notice',
487488
118: 'contact',
@@ -537,6 +538,12 @@ def _key_as_int(cls, key):
537538
return key
538539
elif isinstance(key, str) and key.lower() in c_datasets_r:
539540
return c_datasets_r[key.lower()]
541+
# Backward compatibility: 'credit' is now 'credit line' per IPTC Core 1.1
542+
elif isinstance(key, str) and key.lower() == 'credit':
543+
return 110
544+
# Alias for compatibility with gThumb/exiftool
545+
elif isinstance(key, str) and key.lower() == 'destination':
546+
return 103 # Maps to 'original transmission reference'
540547
elif key.startswith(cls.c_cust_pre) and key[len(cls.c_cust_pre):].isdigit():
541548
# example: nonstandard_69 -> 69
542549
return int(key[len(cls.c_cust_pre):])
@@ -606,6 +613,7 @@ def __init__(self, fobj, force=False, inp_charset=None, out_charset=None):
606613
'contact': [],
607614
})
608615
self._fobj = fobj
616+
self._force = force
609617
if duck_typed(fobj, 'read'): # DELETEME
610618
self._filename = None
611619
else:
@@ -765,7 +773,11 @@ def jpegScan(self, fh):
765773
err = "jpeg_skip_variable failed"
766774
if err is not None:
767775
self.error = err
768-
logger.warning(err)
776+
# When force=True, log as INFO instead of WARNING since we expect no IPTC data
777+
if self._force:
778+
logger.info(err)
779+
else:
780+
logger.warning(err)
769781
return None
770782

771783
# If were's here, we must have found the right marker.
@@ -800,15 +812,32 @@ def blindScan(self, fh, MAX=819200):
800812
# found character set's record!
801813
try:
802814
temp = read_exactly(fh, jpeg_get_variable_length(fh))
803-
try:
804-
cs = unpack('!H', temp)[0]
805-
except Exception: # TODO better exception
806-
logger.warning('WARNING: problems with charset recognition (%r)', temp)
807-
cs = None
808-
if cs in c_charset:
809-
self.inp_charset = c_charset[cs]
810-
logger.info("BlindScan: found character set '%s' at offset %d",
811-
self.inp_charset, offset)
815+
cs = None
816+
# Check for ISO 2022 escape sequence (starts with ESC 0x1b)
817+
if len(temp) >= 3 and ord3(temp[0]) == 0x1b:
818+
# Parse ISO 2022 escape sequences
819+
# ESC % G = UTF-8
820+
if temp == b'\x1b%G':
821+
self.inp_charset = 'utf_8'
822+
# ESC % / @ = UTF-16 (not commonly used)
823+
elif temp == b'\x1b%/@':
824+
self.inp_charset = 'utf_16'
825+
else:
826+
logger.debug(
827+
"BlindScan: unknown ISO 2022 charset escape sequence %r",
828+
temp)
829+
else:
830+
# Try legacy numeric charset encoding
831+
try:
832+
cs = unpack('!H', temp)[0]
833+
if cs in c_charset:
834+
self.inp_charset = c_charset[cs]
835+
except Exception:
836+
logger.debug('BlindScan: could not parse charset from %r', temp)
837+
838+
if self.inp_charset:
839+
logger.info("BlindScan: found character set '%s' at offset %d",
840+
self.inp_charset, offset)
812841
except EOFException:
813842
pass
814843

@@ -902,7 +931,18 @@ def packedIIMData(self):
902931
LOGDBG.debug('out=%s', hex_dump(out))
903932
# Iterate over data sets
904933
for dataset, value in self._data.items():
905-
if len(value) == 0:
934+
# Skip None, empty strings, empty lists, and NaN values
935+
if value is None:
936+
continue
937+
# Handle float/int that might be NaN
938+
if isinstance(value, (float, int)):
939+
import math
940+
if isinstance(value, float) and math.isnan(value):
941+
continue
942+
# Convert numeric values to strings
943+
value = str(value)
944+
# Check length for strings and lists
945+
if hasattr(value, '__len__') and len(value) == 0:
906946
continue
907947

908948
if not (isinstance(dataset, int) and dataset in c_datasets):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def openfile(fname):
5555
maintainer='James Campbell',
5656
maintainer_email='jc@normail.co',
5757
long_description=long_description,
58-
license='http://www.opensource.org/licenses/gpl-license.php',
58+
license='Artistic-1.0 OR GPL-1.0-or-later',
5959
platforms=['any'],
6060
description="""A great way to get IPTCInfo""",
6161
classifiers=[_f for _f in classifiers.split('\n') if _f],

0 commit comments

Comments
 (0)