diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4927f36..2c77f06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,7 +87,7 @@ jobs: fail-fast: false matrix: os: [macos-12] - python_version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] + python_version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] exiv2_version: [0.27.7] runs-on: ${{matrix.os}} env: @@ -159,7 +159,7 @@ jobs: fail-fast: false matrix: os: [windows-2019] - python_version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] + python_version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] exiv2_version: [0.27.7] runs-on: ${{matrix.os}} env: diff --git a/.github/workflows/make_package.sh b/.github/workflows/make_package.sh index ab08215..a4373b8 100644 --- a/.github/workflows/make_package.sh +++ b/.github/workflows/make_package.sh @@ -49,7 +49,7 @@ rm -rf $DIST_DIR # mv $whl_name ${whl_name/-none-any/} make_wheels(){ - for py_version in {5..12} + for py_version in {6..12} do reset_workdir rm -rf $TEST_DIR diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7028df..25d02a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [macos-12, macos-13, windows-2019] - python_version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] + python_version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test_package.yml b/.github/workflows/test_package.yml index dac4437..8160552 100644 --- a/.github/workflows/test_package.yml +++ b/.github/workflows/test_package.yml @@ -24,7 +24,7 @@ jobs: python-version: ${{ matrix.python_version }} - name: Install dependencies run: | - python -m pip install pyexiv2==2.8.3 + python -m pip install pyexiv2==2.9.0 python -m pip install pytest psutil - name: Test run: | @@ -50,7 +50,7 @@ jobs: python-version: ${{ matrix.python_version }} - name: Install dependencies run: | - python -m pip install pyexiv2==2.8.3 + python -m pip install pyexiv2==2.9.0 python -m pip install pytest psutil - name: Test run: | diff --git a/README.md b/README.md index 0c50863..d35d5a6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Read/Write metadata(including [EXIF](https://en.wikipedia.org/wiki/Exif), [IPTC] ## Features - Base on C++ API of [Exiv2](https://exiv2.org/index.html) and wrapped with [pybind11](https://github.com/pybind/pybind11). -- Supports running on 64bit Linux, MacOS and Windows, with CPython(≥3.5) interpreter. +- Supports running on 64bit Linux, MacOS and Windows, with CPython(≥3.6) interpreter. - [Supports various image metadata](https://exiv2.org/metadata.html) - [Supports various image formats](https://dev.exiv2.org/projects/exiv2/wiki/Supported_image_formats) - Supports opening images based on the file path or from bytes data. diff --git a/docs/Tutorial-cn.md b/docs/Tutorial-cn.md index 8cd6569..1a3c889 100644 --- a/docs/Tutorial-cn.md +++ b/docs/Tutorial-cn.md @@ -8,7 +8,7 @@ - 你可以执行 `pip install pyexiv2` 来安装 pyexiv2 。它包含一些已编译的库文件,带有以下兼容条件: - 操作系统为 Linux、MacOS 或 Windows - CPU 架构为 AMD64 - - Python 解释器为 CPython(≥3.5) + - Python 解释器为 CPython(≥3.6) - 如果你想在其它平台上运行 pyexiv2 ,你可以下载源代码然后编译它。参考 [pyexiv2/lib](https://github.com/LeoHsiao1/pyexiv2/blob/master/pyexiv2/lib/README.md)。 ### 常见问题 @@ -180,13 +180,6 @@ def set_log_level(level=2) >>> img.read_exif()['Exif.Photo.MakerNote'] '' ``` - ```py - >>> img.read_xmp()['Xmp.xmpMM.History'] - 'type="Seq"' - >>> img.modify_xmp({'Xmp.xmpMM.History': 'type="Seq"'}) - RuntimeError: XMP Toolkit error 102: Indexing applied to non-array - Failed to encode XMP metadata. - ``` ### clear diff --git a/docs/Tutorial.md b/docs/Tutorial.md index bd8b1a7..c80b184 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -8,7 +8,7 @@ Language: [English](./Tutorial.md) | [中文](./Tutorial-cn.md) - You can execute `pip install pyexiv2` to install pyexiv2. It contains some compiled library files with the following compatibility conditions: - The operating system is Linux, MacOS, or Windows - The CPU architecture is AMD64 - - The Python interpreter is CPython(≥3.5) + - The Python interpreter is CPython(≥3.6) - If you want to run pyexiv2 on another platform, You can download the source code and compile it. See [pyexiv2/lib](https://github.com/LeoHsiao1/pyexiv2/blob/master/pyexiv2/lib/README.md). ### FAQ @@ -180,13 +180,6 @@ def set_log_level(level=2) >>> img.read_exif()['Exif.Photo.MakerNote'] '' ``` - ```py - >>> img.read_xmp()['Xmp.xmpMM.History'] - 'type="Seq"' - >>> img.modify_xmp({'Xmp.xmpMM.History': 'type="Seq"'}) - RuntimeError: XMP Toolkit error 102: Indexing applied to non-array - Failed to encode XMP metadata. - ``` ### clear diff --git a/pyexiv2/__init__.py b/pyexiv2/__init__.py index 6b22a33..791712e 100644 --- a/pyexiv2/__init__.py +++ b/pyexiv2/__init__.py @@ -6,7 +6,7 @@ from .core import * -__version__ = '2.8.3' +__version__ = '2.9.0' __exiv2_version__ = exiv2api.version() diff --git a/pyexiv2/core.py b/pyexiv2/core.py index e60911d..25fb565 100644 --- a/pyexiv2/core.py +++ b/pyexiv2/core.py @@ -86,7 +86,7 @@ def read_thumbnail(self) -> bytes: return self.img.read_thumbnail() def modify_exif(self, data: dict, encoding='utf-8'): - # Encode some tags + data = data.copy() for tag in reference.EXIF_TAGS_ENCODED_IN_UCS2: value = data.get(tag) if value: @@ -123,7 +123,12 @@ def _parse(self, table: list, encoding='utf-8') -> dict: tag, value, typeName = [field.decode(encoding) for field in line] if typeName in ['XmpBag', 'XmpSeq']: value = value.split(', ') + elif typeName in ['XmpText']: + # Handle nested array structures in XML. Refer to https://exiv2.org/manpage.html#set_xmp_struct + if value in ['type="Bag"', 'type="Seq"']: + value = [''] elif typeName in ['LangAlt']: + # Refer to https://exiv2.org/manpage.html#langalt_values if 'lang=' in value: fields = re.split(r', (lang="\S+") ', ', ' + value)[1:] value = {language: content for language, content in zip(fields[0::2], fields[1::2])} diff --git a/pyexiv2/lib/__init__.py b/pyexiv2/lib/__init__.py index 4142012..0141330 100644 --- a/pyexiv2/lib/__init__.py +++ b/pyexiv2/lib/__init__.py @@ -8,7 +8,7 @@ raise RuntimeError('pyexiv2 can only run on 64-bit python3 interpreter.') py_version = platform.python_version() # the version is a string, like '3.9.7' py_version = '.'.join(py_version.split('.')[:2]) # ignore the patch version -expected_py_version = ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] +expected_py_version = ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] if py_version not in expected_py_version: raise RuntimeError('pyexiv2 only supports these Python versions: {} . But your version is {} .'.format(expected_py_version, py_version)) diff --git a/pyexiv2/tests/data/data.py b/pyexiv2/tests/data/data.py index 04767ba..cdc816e 100644 --- a/pyexiv2/tests/data/data.py +++ b/pyexiv2/tests/data/data.py @@ -75,7 +75,7 @@ 'Xmp.xmpMM.InstanceID' : 'xmp.iid:14282d1f-7831-7043-ad77-1e10959ecd50', 'Xmp.xmpMM.DocumentID' : 'ECE1099AF3406874FAA7B01CBB5C6F71', 'Xmp.xmpMM.OriginalDocumentID' : 'ECE1099AF3406874FAA7B01CBB5C6F71', - 'Xmp.xmpMM.History' : 'type="Seq"', + 'Xmp.xmpMM.History' : [''], 'Xmp.xmpMM.History[1]' : 'type="Struct"', 'Xmp.xmpMM.History[1]/stEvt:action' : 'saved', 'Xmp.xmpMM.History[1]/stEvt:instanceID' : 'xmp.iid:8f83ee32-1163-7b40-b31b-deab20789cf4', diff --git a/pyexiv2/tests/test_func.py b/pyexiv2/tests/test_func.py index 46dae95..5d255dc 100644 --- a/pyexiv2/tests/test_func.py +++ b/pyexiv2/tests/test_func.py @@ -93,6 +93,7 @@ def test_read_thumbnail(): def test_modify_exif(): + # Test writing key and deleting key changes = {'Exif.Image.ImageDescription': 'test-中文-', 'Exif.Image.Artist': None} ENV.img.modify_exif(changes) @@ -100,6 +101,7 @@ def test_modify_exif(): # Check the modified data expected_result = simulate_updating_metadata(data.EXIF, changes) result = ENV.img.read_exif() + # Somehow, the value of ['Exif.Image.ExifTag', 'Exif.Thumbnail.JPEGInterchangeFormat'] will change ignored_keys = ['Exif.Image.ExifTag', 'Exif.Thumbnail.JPEGInterchangeFormat'] for key in ignored_keys: expected_result.pop(key) @@ -130,9 +132,8 @@ def test_modify_iptc(): def test_modify_xmp(): changes = {'Xmp.xmp.CreateDate': '2019-06-23T19:45:17.834', - 'Xmp.xmp.Rating': None, - 'Xmp.iptc.Location': 'somewhere', - 'Xmp.dc.subject': ['tag1', 'tag2', 'tag3']} + 'Xmp.xmp.Rating': None, + 'Xmp.dc.subject': ['tag1', 'tag2', 'tag3']} ENV.img.modify_xmp(changes) expected_result = simulate_updating_metadata(data.XMP, changes) diff_dict(expected_result, ENV.img.read_xmp()) @@ -147,6 +148,21 @@ def test_modify_raw_xmp(): test_read_xmp() +def test_modify_exif_all(): + ENV.img.modify_exif(data.EXIF) + diff_dict(data.EXIF, ENV.img.read_exif()) + + +def test_modify_iptc_all(): + ENV.img.modify_iptc(data.IPTC) + diff_dict(data.IPTC, ENV.img.read_iptc()) + + +def test_modify_xmp_all(): + ENV.img.modify_xmp(data.XMP) + diff_dict(data.XMP, ENV.img.read_xmp()) + + def test_modify_comment(): comment = 'Hello! \n你好!\n' * 1000 ENV.img.modify_comment(comment) diff --git a/setup.py b/setup.py index 2db9e42..7c22d1f 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='pyexiv2', - version='2.8.3', # need to set the variable in 'pyexiv2/__init__.py' + version='2.9.0', # need to set the variable in 'pyexiv2/__init__.py' author='LeoHsiao', author_email='leohsiao@foxmail.com', description='Read/Write metadata(including EXIF, IPTC, XMP), comment and ICC Profile embedded in digital images.', @@ -19,7 +19,7 @@ packages=setuptools.find_packages(), # packages=['pyexiv2', 'docs'], package_data={'': ['*', '*/*']}, - python_requires='>=3.5', + python_requires='>=3.6', # install_requires=["pybind11"], classifiers=[ 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',