├── .github └── workflows │ ├── build-linux-27.yml │ ├── build-linux-28.yml │ ├── build-macos-27.yml │ ├── build-macos-28.yml │ ├── build-macos-arm-27.yml │ ├── build-macos-arm-28.yml │ ├── build-windows-27.yml │ ├── build-windows-28.yml │ └── test-interface.yml ├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.txt ├── INSTALL.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── USAGE.rst ├── examples ├── README.rst ├── addmoddel.py ├── easyaccess.py ├── exifcomment.py ├── exifdata.py ├── exifprint.py ├── exifvalue.py ├── iptceasy.py ├── thumbnail.py ├── values.py └── xmpsample.py ├── pyproject.toml ├── setup.py ├── src ├── doc │ ├── _templates │ │ └── module.rst │ ├── conf.py │ ├── index.rst │ ├── misc │ │ ├── api.rst │ │ ├── changelog.rst │ │ ├── install.rst │ │ ├── readme.rst │ │ └── usage.rst │ └── requirements.txt ├── interface │ ├── __main__.py │ ├── basicio.i │ ├── datasets.i │ ├── easyaccess.i │ ├── error.i │ ├── exif.i │ ├── image.i │ ├── iptc.i │ ├── metadatum.i │ ├── preview.i │ ├── properties.i │ ├── shared │ │ ├── buffers.i │ │ ├── containers.i │ │ ├── data_iterator.i │ │ ├── enum.i │ │ ├── exception.i │ │ ├── exv_options.i │ │ ├── keep_reference.i │ │ ├── preamble.i │ │ ├── static_list.i │ │ ├── struct_dict.i │ │ ├── unique_ptr.i │ │ ├── windows_cp.i │ │ └── windows_path.i │ ├── tags.i │ ├── types.i │ ├── value.i │ ├── version.i │ └── xmp.i ├── swig-0_27_7 │ ├── __init__.py │ ├── __main__.py │ ├── basicio.py │ ├── basicio_wrap.cxx │ ├── datasets.py │ ├── datasets_wrap.cxx │ ├── easyaccess.py │ ├── easyaccess_wrap.cxx │ ├── error.py │ ├── error_wrap.cxx │ ├── exif.py │ ├── exif_wrap.cxx │ ├── image.py │ ├── image_wrap.cxx │ ├── iptc.py │ ├── iptc_wrap.cxx │ ├── metadatum.py │ ├── metadatum_wrap.cxx │ ├── preview.py │ ├── preview_wrap.cxx │ ├── properties.py │ ├── properties_wrap.cxx │ ├── tags.py │ ├── tags_wrap.cxx │ ├── types.py │ ├── types_wrap.cxx │ ├── value.py │ ├── value_wrap.cxx │ ├── version.py │ ├── version_wrap.cxx │ ├── xmp.py │ └── xmp_wrap.cxx └── swig-0_28_5 │ ├── __init__.py │ ├── __main__.py │ ├── basicio.py │ ├── basicio_wrap.cxx │ ├── datasets.py │ ├── datasets_wrap.cxx │ ├── easyaccess.py │ ├── easyaccess_wrap.cxx │ ├── error.py │ ├── error_wrap.cxx │ ├── exif.py │ ├── exif_wrap.cxx │ ├── image.py │ ├── image_wrap.cxx │ ├── iptc.py │ ├── iptc_wrap.cxx │ ├── metadatum.py │ ├── metadatum_wrap.cxx │ ├── preview.py │ ├── preview_wrap.cxx │ ├── properties.py │ ├── properties_wrap.cxx │ ├── tags.py │ ├── tags_wrap.cxx │ ├── types.py │ ├── types_wrap.cxx │ ├── value.py │ ├── value_wrap.cxx │ ├── version.py │ ├── version_wrap.cxx │ ├── xmp.py │ └── xmp_wrap.cxx ├── tests ├── image_01.jpg ├── image_02.heic ├── image_02.jpg ├── test_basicio.py ├── test_datasets.py ├── test_easyaccess.py ├── test_error.py ├── test_exif.py ├── test_image.py ├── test_iptc.py ├── test_metadatum.py ├── test_preview.py ├── test_properties.py ├── test_tags.py ├── test_types.py ├── test_value.py ├── test_version.py └── test_xmp.py └── utils ├── build_docs.py ├── build_swig.py └── tag_release.py /.github/workflows/build-linux-27.yml: -------------------------------------------------------------------------------- 1 | name: Build Linux wheels exiv2 0.27 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Exiv2 source 12 | run: > 13 | wget -nv 14 | https://github.com/Exiv2/exiv2/releases/download/v0.27.7/exiv2-0.27.7-Source.tar.gz 15 | -O exiv2.tar.gz 16 | 17 | - name: Extract Exiv2 source 18 | shell: bash 19 | run: | 20 | tar -xzf exiv2.tar.gz 21 | mv exiv2-0.27.7-Source libexiv2 22 | 23 | - name: Build wheels 24 | uses: pypa/cibuildwheel@v2.16.5 25 | env: 26 | CIBW_ARCHS: auto64 27 | CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 28 | CIBW_ENVIRONMENT: EXIV2_ROOT=libexiv2/build-linux/install 29 | CIBW_BUILD: cp*-manylinux_x86_64 30 | CIBW_TEST_COMMAND: > 31 | python3 -m exiv2 -v && 32 | python3 -m unittest discover {project}/tests -v 33 | CIBW_BEFORE_ALL: > 34 | yum install -y zlib-devel expat-devel gettext-devel 35 | libcurl-devel && 36 | localedef -c -i de_DE -f UTF-8 de_DE.UTF-8 && 37 | cd libexiv2 && 38 | cmake -B build-linux -D CMAKE_BUILD_TYPE=Release 39 | -D CMAKE_INSTALL_PREFIX=build-linux/install 40 | -D CMAKE_CXX_FLAGS="-Wno-deprecated-declarations" 41 | -D EXIV2_BUILD_SAMPLES=OFF 42 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 43 | -D EXIV2_ENABLE_BMFF=ON 44 | -D EXIV2_ENABLE_NLS=ON 45 | -D EXIV2_ENABLE_VIDEO=ON 46 | -D EXIV2_ENABLE_WEBREADY=ON 47 | -D EXIV2_ENABLE_CURL=ON 48 | -D EXIV2_ENABLE_SSH=OFF 49 | -D CMAKE_CXX_STANDARD=98 && 50 | cmake --build build-linux && 51 | cmake --install build-linux 52 | 53 | - name: Store results 54 | uses: actions/upload-artifact@v3 55 | with: 56 | name: linux-27-wheels 57 | path: wheelhouse/*.whl 58 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-28.yml: -------------------------------------------------------------------------------- 1 | name: Build Linux wheels exiv2 0.28 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Exiv2 source 12 | run: > 13 | wget -nv 14 | https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.5.tar.gz 15 | -O exiv2.tar.gz 16 | 17 | - name: Extract Exiv2 source 18 | run: | 19 | tar -xzf exiv2.tar.gz 20 | mv exiv2-0.28.5 libexiv2 21 | 22 | - name: Download inih 23 | run: > 24 | wget -nv 25 | https://github.com/benhoyt/inih/archive/r58/inih-r58.tar.gz 26 | -O inih.tar.gz 27 | 28 | - name: Extract inih source 29 | run: | 30 | tar -xzf inih.tar.gz 31 | mv inih-r58 inih 32 | 33 | - name: Build wheels 34 | uses: pypa/cibuildwheel@v2.22.0 35 | env: 36 | CIBW_ARCHS: auto64 37 | CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 38 | CIBW_ENVIRONMENT: EXIV2_ROOT=libexiv2/build-linux-release/install 39 | CIBW_BUILD: cp*-manylinux_x86_64 40 | CIBW_TEST_COMMAND: > 41 | python3 -m exiv2 -v && 42 | python3 -m unittest discover {project}/tests -v 43 | CIBW_BEFORE_ALL: > 44 | yum install -y --nogpgcheck zlib-devel expat-devel gettext-devel 45 | libcurl-devel brotli-devel meson && 46 | localedef -c -i de_DE -f UTF-8 de_DE.UTF-8 && 47 | pip install ninja && 48 | cd inih && mkdir build && cd build && 49 | meson setup --prefix=/usr --buildtype=release .. && 50 | ninja && ninja install && 51 | cd ../../libexiv2 && 52 | cmake --preset linux-release 53 | -D CONAN_AUTO_INSTALL=OFF 54 | -D EXIV2_BUILD_SAMPLES=OFF 55 | -D EXIV2_BUILD_UNIT_TESTS=OFF 56 | -D EXIV2_ENABLE_NLS=ON 57 | -D EXIV2_ENABLE_FILESYSTEM_ACCESS=ON 58 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 59 | -D EXIV2_TEAM_WARNINGS_AS_ERRORS=OFF && 60 | cmake --build build-linux-release --config Release && 61 | cmake --install build-linux-release --config Release 62 | 63 | - name: Store results 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: linux-28-wheels 67 | path: wheelhouse/*.whl 68 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-27.yml: -------------------------------------------------------------------------------- 1 | name: Build MacOS wheels exiv2 0.27 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | build: 6 | runs-on: macos-latest 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Exiv2 source 12 | run: > 13 | wget -nv 14 | https://github.com/Exiv2/exiv2/releases/download/v0.27.7/exiv2-0.27.7-Source.tar.gz 15 | -O exiv2.tar.gz 16 | 17 | - name: Extract Exiv2 source 18 | shell: bash 19 | run: | 20 | tar -xzf exiv2.tar.gz 21 | mv exiv2-0.27.7-Source libexiv2 22 | 23 | - name: Build wheels 24 | uses: pypa/cibuildwheel@v2.16.5 25 | env: 26 | CIBW_ARCHS: x86_64 27 | CIBW_ENVIRONMENT: EXIV2_ROOT=libexiv2/build-macos/install 28 | CIBW_SKIP: pp3* 29 | CIBW_TEST_COMMAND: > 30 | python -m exiv2 -v && 31 | python -m unittest discover {project}/tests -v 32 | CIBW_BEFORE_ALL: > 33 | brew install ninja gettext && 34 | cd libexiv2 && 35 | cmake . -B build-macos -DCMAKE_BUILD_TYPE=Release 36 | -D CMAKE_INSTALL_PREFIX=build-macos/install 37 | -D CMAKE_CXX_FLAGS="-Wno-deprecated-declarations" 38 | -D EXIV2_BUILD_SAMPLES=OFF 39 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 40 | -D EXIV2_ENABLE_BMFF=ON 41 | -D EXIV2_ENABLE_NLS=ON 42 | -D EXIV2_ENABLE_VIDEO=ON 43 | -D EXIV2_ENABLE_WEBREADY=ON 44 | -D EXIV2_ENABLE_CURL=ON 45 | -D EXIV2_ENABLE_SSH=OFF 46 | -D CMAKE_CXX_STANDARD=98 47 | -G Ninja && 48 | cmake --build build-macos --config Release && 49 | cmake --install build-macos --config Release 50 | 51 | - name: Store results 52 | uses: actions/upload-artifact@v3 53 | with: 54 | name: macos-27-wheels 55 | path: wheelhouse/*.whl 56 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-28.yml: -------------------------------------------------------------------------------- 1 | name: Build MacOS wheels exiv2 0.28 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | build: 6 | runs-on: macos-13 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Exiv2 source 12 | run: > 13 | wget -nv 14 | https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.5.tar.gz 15 | -O exiv2.tar.gz 16 | 17 | - name: Extract Exiv2 source 18 | shell: bash 19 | run: | 20 | tar -xzf exiv2.tar.gz 21 | mv exiv2-0.28.5 libexiv2 22 | 23 | - name: Build wheels 24 | uses: pypa/cibuildwheel@v2.22.0 25 | env: 26 | CIBW_ARCHS: x86_64 27 | CIBW_ENVIRONMENT: > 28 | EXIV2_ROOT=libexiv2/build-base_mac/install 29 | MACOSX_DEPLOYMENT_TARGET="13.0" 30 | SYSTEM_VERSION_COMPAT=0 31 | CIBW_SKIP: pp3* 32 | CIBW_TEST_COMMAND: > 33 | python -m exiv2 -v && 34 | python -m unittest discover {project}/tests -v 35 | CIBW_BEFORE_ALL: > 36 | brew install ninja inih && 37 | cd libexiv2 && 38 | cmake --preset base_mac 39 | -D EXIV2_BUILD_SAMPLES=OFF 40 | -D EXIV2_BUILD_UNIT_TESTS=OFF 41 | -D EXIV2_ENABLE_NLS=ON 42 | -D EXIV2_ENABLE_FILESYSTEM_ACCESS=ON 43 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 44 | -D EXIV2_TEAM_WARNINGS_AS_ERRORS=OFF && 45 | cmake --build build-base_mac --config Release && 46 | cmake --install build-base_mac --config Release 47 | 48 | - name: Store results 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: macos-28-wheels 52 | path: wheelhouse/*.whl 53 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-arm-27.yml: -------------------------------------------------------------------------------- 1 | name: Build MacOS ARM wheels exiv2 0.27 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | build: 6 | runs-on: flyci-macos-large-latest-m1 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Exiv2 source 12 | run: > 13 | wget -nv 14 | https://github.com/Exiv2/exiv2/releases/download/v0.27.7/exiv2-0.27.7-Source.tar.gz 15 | -O exiv2.tar.gz 16 | 17 | - name: Extract Exiv2 source 18 | shell: bash 19 | run: | 20 | tar -xzf exiv2.tar.gz 21 | mv exiv2-0.27.7-Source libexiv2 22 | 23 | - name: Install pipx 24 | run: pip install pipx 25 | 26 | - name: Build wheels 27 | uses: pypa/cibuildwheel@v2.16.5 28 | env: 29 | CIBW_ARCHS: arm64 30 | CIBW_ENVIRONMENT: EXIV2_ROOT=libexiv2/build-macos/install 31 | CIBW_SKIP: pp3* 32 | CIBW_TEST_COMMAND: > 33 | python -m exiv2 -v && 34 | python -m unittest discover {project}/tests -v 35 | CIBW_TEST_SKIP: cp38-* 36 | CIBW_BEFORE_ALL: > 37 | brew install ninja curl && 38 | cd libexiv2 && 39 | cmake . -B build-macos -DCMAKE_BUILD_TYPE=Release 40 | -D CMAKE_INSTALL_PREFIX=build-macos/install 41 | -D CMAKE_CXX_FLAGS="-Wno-deprecated-declarations" 42 | -D EXIV2_BUILD_SAMPLES=OFF 43 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 44 | -D EXIV2_ENABLE_BMFF=ON 45 | -D EXIV2_ENABLE_NLS=ON 46 | -D EXIV2_ENABLE_VIDEO=ON 47 | -D EXIV2_ENABLE_WEBREADY=ON 48 | -D EXIV2_ENABLE_CURL=ON 49 | -D EXIV2_ENABLE_SSH=OFF 50 | -D CMAKE_CXX_STANDARD=98 51 | -D CMAKE_OSX_ARCHITECTURES=arm64 52 | -G Ninja && 53 | cmake --build build-macos --config Release && 54 | cmake --install build-macos --config Release 55 | 56 | - name: Store results 57 | uses: actions/upload-artifact@v3 58 | with: 59 | name: macos-arm-27-wheels 60 | path: wheelhouse/*.whl 61 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-arm-28.yml: -------------------------------------------------------------------------------- 1 | name: Build MacOS ARM wheels exiv2 0.28 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | build: 6 | runs-on: macos-latest 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Exiv2 source 12 | run: > 13 | wget -nv 14 | https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.5.tar.gz 15 | -O exiv2.tar.gz 16 | 17 | - name: Extract Exiv2 source 18 | shell: bash 19 | run: | 20 | tar -xzf exiv2.tar.gz 21 | mv exiv2-0.28.5 libexiv2 22 | 23 | - name: Build wheels 24 | uses: pypa/cibuildwheel@v2.22.0 25 | env: 26 | CIBW_ARCHS: arm64 27 | CIBW_ENVIRONMENT: > 28 | EXIV2_ROOT=libexiv2/build-base_mac/install 29 | MACOSX_DEPLOYMENT_TARGET="14.0" 30 | SYSTEM_VERSION_COMPAT=0 31 | CIBW_SKIP: pp3* 32 | CIBW_TEST_COMMAND: > 33 | python -m exiv2 -v && 34 | python -m unittest discover {project}/tests -v 35 | CIBW_TEST_SKIP: cp38-* 36 | CIBW_BEFORE_ALL: > 37 | brew install ninja inih && 38 | cd libexiv2 && 39 | cmake --preset base_mac 40 | -D CMAKE_OSX_ARCHITECTURES=arm64 41 | -D EXIV2_BUILD_SAMPLES=OFF 42 | -D EXIV2_BUILD_UNIT_TESTS=OFF 43 | -D EXIV2_ENABLE_NLS=ON 44 | -D EXIV2_ENABLE_FILESYSTEM_ACCESS=ON 45 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 46 | -D EXIV2_TEAM_WARNINGS_AS_ERRORS=OFF && 47 | cmake --build build-base_mac --config Release && 48 | cmake --install build-base_mac --config Release 49 | 50 | - name: Store results 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: macos-arm-28-wheels 54 | path: wheelhouse/*.whl 55 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-27.yml: -------------------------------------------------------------------------------- 1 | name: Build Windows wheels exiv2 0.27 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | compile: 6 | runs-on: windows-2019 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Gettext 12 | run: > 13 | c:\msys64\usr\bin\wget.exe -nv 14 | https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-64.zip 15 | -O gettext.zip 16 | 17 | - name: Extract Gettext 18 | shell: bash 19 | run: | 20 | mkdir gettext 21 | cd gettext 22 | unzip ../gettext.zip 23 | 24 | - name: Fetch Exiv2 source 25 | run: > 26 | c:\msys64\usr\bin\wget.exe -nv 27 | https://github.com/Exiv2/exiv2/releases/download/v0.27.7/exiv2-0.27.7-Source.tar.gz 28 | -O exiv2.tar.gz 29 | 30 | - name: Extract Exiv2 source 31 | shell: bash 32 | run: | 33 | tar -xzf exiv2.tar.gz 34 | mv exiv2-0.27.7-Source libexiv2 35 | # tweaks to allow NLS 36 | rm libexiv2/cmake/FindIconv.cmake 37 | echo -e "24a25\n> self.requires('libgettext/0.21')" | 38 | c:/msys64/usr/bin/patch.exe libexiv2/conanfile.py 39 | 40 | - name: Build wheels 41 | uses: pypa/cibuildwheel@v2.16.5 42 | env: 43 | CIBW_ARCHS: auto64 44 | CIBW_SKIP: pp3* 45 | CIBW_ENVIRONMENT: | 46 | EXIV2_ROOT=libexiv2/build-msvc/install 47 | PATH="$PATH;$(pwd)\\gettext\\bin" 48 | CIBW_TEST_COMMAND: > 49 | python -m exiv2 -v && 50 | python -m unittest discover {project}/tests -v 51 | CIBW_BEFORE_ALL: > 52 | pip install conan==1.59.0 && 53 | cd libexiv2 && 54 | conan install . -of build-msvc -if build-msvc -o unitTests=False 55 | -o iconv=True -o webready=True -b missing && 56 | cmake -B build-msvc 57 | -D CMAKE_BUILD_TYPE=Release 58 | -D CMAKE_INSTALL_PREFIX=build-msvc/install 59 | -D EXIV2_ENABLE_WIN_UNICODE=OFF 60 | -D EXIV2_BUILD_SAMPLES=OFF 61 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 62 | -D EXIV2_ENABLE_BMFF=ON 63 | -D EXIV2_ENABLE_NLS=ON 64 | -D EXIV2_ENABLE_VIDEO=ON 65 | -D EXIV2_ENABLE_WEBREADY=ON 66 | -D CMAKE_CXX_STANDARD=98 67 | -G "Visual Studio 16 2019" -A x64 && 68 | cmake --build build-msvc --config Release && 69 | cmake --install build-msvc --config Release 70 | 71 | - name: Store results 72 | uses: actions/upload-artifact@v3 73 | with: 74 | name: windows-27-wheels 75 | path: wheelhouse/*.whl 76 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-28.yml: -------------------------------------------------------------------------------- 1 | name: Build Windows wheels exiv2 0.28 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | compile: 6 | runs-on: windows-2019 7 | steps: 8 | - name: Check out repository code 9 | uses: actions/checkout@v4 10 | 11 | - name: Fetch Gettext 12 | run: > 13 | c:\msys64\usr\bin\wget.exe -nv 14 | https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-64.zip 15 | -O gettext.zip 16 | 17 | - name: Extract Gettext 18 | shell: bash 19 | run: | 20 | mkdir gettext 21 | cd gettext 22 | unzip ../gettext.zip 23 | 24 | - name: Fetch Exiv2 source 25 | run: > 26 | c:\msys64\usr\bin\wget.exe -nv 27 | https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.5.tar.gz 28 | -O exiv2.tar.gz 29 | 30 | - name: Extract Exiv2 source 31 | shell: bash 32 | run: | 33 | tar -xzf exiv2.tar.gz 34 | mv exiv2-0.28.5 libexiv2 35 | # tweaks to allow NLS 36 | echo -e "24a25\n> self.requires('libgettext/0.21')" | 37 | c:/msys64/usr/bin/patch.exe libexiv2/conanfile.py 38 | 39 | - name: Install Python 40 | uses: actions/setup-python@v5 41 | with: 42 | python-version: 3.11 43 | 44 | - name: Install cmake 45 | uses: jwlawson/actions-setup-cmake@v2 46 | 47 | - name: Install ninja 48 | uses: seanmiddleditch/gha-setup-ninja@master 49 | 50 | - name: Compile Exiv2 51 | env: 52 | PATH: "${{ github.workspace }}\\gettext\\bin" 53 | run: > 54 | pip install conan==1.59.0 && 55 | cd libexiv2 && 56 | cmake --preset msvc 57 | -D CMAKE_BUILD_TYPE=Release 58 | -D EXIV2_BUILD_SAMPLES=OFF 59 | -D EXIV2_BUILD_EXIV2_COMMAND=OFF 60 | -D EXIV2_BUILD_UNIT_TESTS=OFF 61 | -D EXIV2_ENABLE_NLS=ON 62 | -D EXIV2_ENABLE_FILESYSTEM_ACCESS=ON 63 | -G "Visual Studio 16 2019" && 64 | cmake --build build-msvc --config Release && 65 | cmake --install build-msvc --config Release && 66 | copy build-msvc\bin\libcurl.dll build-msvc\install\bin 67 | 68 | - name: Build wheels 69 | uses: pypa/cibuildwheel@v2.22.0 70 | env: 71 | CIBW_ARCHS: auto64 72 | CIBW_SKIP: pp3* 73 | CIBW_ENVIRONMENT: | 74 | EXIV2_ROOT=libexiv2/build-msvc/install 75 | CIBW_TEST_COMMAND: > 76 | python -m exiv2 -v && 77 | python -m unittest discover {project}/tests -v 78 | 79 | - name: Store results 80 | uses: actions/upload-artifact@v4 81 | with: 82 | name: windows-28-wheels 83 | path: wheelhouse/*.whl 84 | -------------------------------------------------------------------------------- /.github/workflows/test-interface.yml: -------------------------------------------------------------------------------- 1 | # python-exiv2 - Python interface to libexiv2 2 | # http://github.com/jim-easterbrook/python-exiv2 3 | # Copyright (C) 2022-25 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU General Public License as published by the Free Software 7 | # Foundation, either version 3 of the License, or (at your option) any later 8 | # version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT 11 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU General Public License along with 16 | # this program. If not, see . 17 | 18 | 19 | name: Run unit tests 20 | on: 21 | push: 22 | paths: 23 | - 'src/swig_0.27.0/**' 24 | - 'tests/**.py' 25 | workflow_dispatch: 26 | 27 | jobs: 28 | test: 29 | strategy: 30 | matrix: 31 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] 32 | os: [ubuntu-latest] 33 | include: 34 | - python-version: '3.6' 35 | os: ubuntu-20.04 36 | - python-version: '3.7' 37 | os: ubuntu-22.04 38 | runs-on: ${{ matrix.os }} 39 | steps: 40 | - name: Check out repository code 41 | uses: actions/checkout@v3 42 | 43 | - name: Install libexiv2 44 | run: | 45 | sudo apt-get update 46 | sudo apt-get install libexiv2-dev 47 | 48 | - name: Setup Python 49 | uses: actions/setup-python@v4 50 | with: 51 | python-version: ${{ matrix.python-version }} 52 | architecture: 'x64' 53 | 54 | - name: Install python-exiv2 55 | run: pip3 install --disable-pip-version-check --user . 56 | 57 | - name: Get package details 58 | run: python3 -m exiv2 -v 59 | 60 | - name: Run unit tests 61 | run: python3 -m unittest discover tests -v 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /doc 3 | /dist 4 | /src/doc/api 5 | exiv2.egg-info 6 | __pycache__ 7 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.11" 12 | apt_packages: 13 | - graphviz 14 | 15 | # Build documentation in the "docs/" directory with Sphinx 16 | sphinx: 17 | configuration: src/doc/conf.py 18 | 19 | python: 20 | install: 21 | - requirements: src/doc/requirements.txt 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src * 2 | recursive-include utils * 3 | recursive-include examples * 4 | recursive-include tests *.py *.jpg 5 | 6 | recursive-exclude libexiv2* * 7 | recursive-exclude src/doc/api * 8 | 9 | include CHANGELOG.txt LICENSE *.rst 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | python-exiv2 v\ 0.17.3 2 | ====================== 3 | 4 | python-exiv2 is a low level interface (or binding) to the exiv2_ C++ library. 5 | It is built using SWIG_ to automatically generate the interface code. 6 | The intention is to give direct access to all of the top-level classes in libexiv2_, but with additional "Pythonic" helpers where necessary. 7 | Not everything in libexiv2 is available in the Python interface. 8 | If you need something that's not there, please let me know. 9 | 10 | .. note:: 11 | This project has taken over the PyPI exiv2 package created by Michael Vanslembrouck. 12 | If you need to use Michael's project, it is available at https://bitbucket.org/zmic/exiv2-python/src/master/ and can be installed with pip_:: 13 | 14 | pip install exiv2==0.3.1 15 | 16 | .. contents:: 17 | :backlinks: top 18 | 19 | Introduction 20 | ------------ 21 | 22 | There are several other ways to access libexiv2_ from within Python. 23 | The first one I used was `pyexiv2 (old)`_. 24 | After its development ceased I moved on to using gexiv2_ and PyGObject_. 25 | This works well, providing a ``Metadata`` object with high level functions such as ``set_tag_string`` and ``set_tag_multiple`` to get and set metadata values. 26 | 27 | A more recent development is `pyexiv2 (new)`_. 28 | This new project is potentially very useful, providing a simple interface with functions to read and modify metadata using Python ``dict`` parameters. 29 | 30 | For more complicated metadata operations I think a lower level interface is required, which is where this project comes in. 31 | Here is an example of its use: 32 | 33 | .. code:: python 34 | 35 | Python 3.6.12 (default, Dec 02 2020, 09:44:23) [GCC] on linux 36 | Type "help", "copyright", "credits" or "license" for more information. 37 | >>> import exiv2 38 | >>> image = exiv2.ImageFactory.open('IMG_0211.JPG') 39 | >>> image.readMetadata() 40 | >>> data = image.exifData() 41 | >>> data['Exif.Image.Artist'].print() 42 | 'Jim Easterbrook' 43 | >>> 44 | 45 | Please see `USAGE.rst`_ for more help with using the Python interface to libexiv2. 46 | 47 | Transition to libexiv2 v0.28.x 48 | ------------------------------ 49 | 50 | Before python-exiv2 v0.16 the "binary wheels" available from PyPI_ incorporated libexiv2 v0.27.7 or earlier. 51 | Binary wheels for python-exiv2 v0.16.3 incorporate libexiv2 v0.28.2, and those for python-exiv2 v0.16.2 incorporate libexiv2 v0.27.7. 52 | Binary wheels for python-exiv2 v0.17.0 incorporate libexiv2 v0.28.3. 53 | If your software is currently incompatible with libexiv2 v0.28.x you can use the older version of libexiv2 by explicitly installing python-exiv2 v0.16.2:: 54 | 55 | $ pip install --user exiv2==0.16.2 56 | 57 | There are some changes in the libexiv2 API between v0.27.7 and v0.28.x. 58 | Future versions of python-exiv2 will all incorporate libexiv2 v0.28.x, so please update your software to use the changed API. 59 | 60 | Documentation 61 | ------------- 62 | 63 | The libexiv2_ library is well documented for C++ users, in Doxygen_ format. 64 | Recent versions of SWIG_ can convert this documentation to pydoc_ format in the Python interface:: 65 | 66 | $ pydoc3 exiv2.Image.readMetadata 67 | Help on method_descriptor in exiv2.Image: 68 | 69 | exiv2.Image.readMetadata = readMetadata(...) 70 | Read all metadata supported by a specific image format from the 71 | image. Before this method is called, the image metadata will be 72 | cleared. 73 | 74 | This method returns success even if no metadata is found in the 75 | image. Callers must therefore check the size of individual metadata 76 | types before accessing the data. 77 | 78 | :raises: Error if opening or reading of the file fails or the image 79 | data is not valid (does not look like data of the specific image 80 | type). 81 | 82 | This is then converted to web pages by Sphinx_ and hosted on ReadTheDocs_. 83 | 84 | Unfortunately some documentation gets lost in the manipulations needed to make a useful interface. 85 | The C++ documentation is still needed in these cases. 86 | 87 | Support for bmff files (e.g. CR3, HEIF, HEIC, AVIF, JPEG XL) 88 | ------------------------------------------------------------ 89 | 90 | Python-exiv2 from version 0.17.0 has support for BMFF files enabled by default if libexiv2 was compiled with support for BMFF files enabled. 91 | In earlier versions you need to call the ``enableBMFF`` function before using BMFF files in your program. 92 | Use of BMFF files may infringe patents. 93 | Please read the Exiv2 `statement on BMFF`_ patents before doing so. 94 | 95 | Installation 96 | ------------ 97 | 98 | Python "binary wheels" are available for Windows, Linux, and MacOS. 99 | These include the libexiv2 library and should not need any other software to be installed. 100 | They can be installed with Python's pip_ package. 101 | For example, on Windows:: 102 | 103 | C:\Users\Jim>pip install exiv2 104 | 105 | or on Linux or MacOS:: 106 | 107 | $ pip3 install --user exiv2 108 | 109 | If the available wheels are not compatible with your operating system or Python version then pip will download the python-exiv2 source and attempt to compile it. 110 | For more information, and details of how to compile python-exiv2 and libexiv2, see `INSTALL.rst`_. 111 | 112 | Problems? 113 | --------- 114 | 115 | Please email jim@jim-easterbrook.me.uk if you find any problems (or solutions!). 116 | 117 | .. _Doxygen: https://www.doxygen.nl/ 118 | .. _exiv2: https://www.exiv2.org/getting-started.html 119 | .. _gexiv2: https://wiki.gnome.org/Projects/gexiv2 120 | .. _GitHub: https://github.com/jim-easterbrook/python-exiv2 121 | .. _libexiv2: https://www.exiv2.org/doc/index.html 122 | .. _pip: https://pip.pypa.io/ 123 | .. _pyexiv2 (new): https://github.com/LeoHsiao1/pyexiv2 124 | .. _pyexiv2 (old): https://launchpad.net/pyexiv2 125 | .. _PyGObject: https://pygobject.readthedocs.io/en/latest/ 126 | .. _PyPI: https://pypi.org/project/exiv2/ 127 | .. _SWIG: http://swig.org/ 128 | .. _pydoc: https://docs.python.org/3/library/pydoc.html 129 | .. _Python3: https://www.python.org/ 130 | .. _ReadTheDocs: https://python-exiv2.readthedocs.io/ 131 | .. _Sphinx: https://www.sphinx-doc.org/ 132 | .. _statement on BMFF: https://github.com/exiv2/exiv2#BMFF 133 | .. _Visual C++: https://wiki.python.org/moin/WindowsCompilers 134 | .. _INSTALL.rst: INSTALL.rst 135 | .. _USAGE.rst: USAGE.rst 136 | -------------------------------------------------------------------------------- /examples/README.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | These example files show some typical (and not so typical) ways to use the Python exiv2 interface. 5 | Some of them are based on the `C++ examples`_ provided by the Exiv2 project. 6 | It might be instructive to compare the C++ and Python ways of doing the same thing. 7 | 8 | .. _C++ examples: https://www.exiv2.org/doc/examples.html 9 | -------------------------------------------------------------------------------- /examples/addmoddel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program showing how to add, modify and delete Exif metadata. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | def main(): 28 | locale.setlocale(locale.LC_ALL, '') 29 | try: 30 | exiv2.XmpParser.initialize() 31 | if len(sys.argv) != 2: 32 | print('Usage: {} file'.format(sys.argv[0])) 33 | return 1; 34 | file = sys.argv[1] 35 | # Container for exif metadata. This is an example of creating 36 | # exif metadata from scratch. If you want to add, modify, delete 37 | # metadata that exists in an image, start with 38 | # ImageFactory::open 39 | exifData = exiv2.ExifData() 40 | 41 | # ************************************************************** 42 | # Add to the Exif data 43 | 44 | # This is the quickest way to add (simple) Exif data. If a 45 | # metadatum for a given key already exists, its value is 46 | # overwritten. Otherwise a new tag is added. 47 | exifData["Exif.Image.Model"] = "Test 1" # AsciiValue 48 | exifData["Exif.Image.SamplesPerPixel"] = exiv2.UShortValue(162) # UShortValue 49 | exifData["Exif.Image.XResolution"] = exiv2.LongValue(-2) # LongValue 50 | exifData["Exif.Image.YResolution"] = exiv2.RationalValue((-2, 3)) # Rational 51 | print("Added a few tags the quick way.") 52 | 53 | # Create a ASCII string value (note the use of create) 54 | v = exiv2.Value.create(exiv2.TypeId.asciiString) 55 | # Set the value to a string 56 | v.read("1999:12:31 23:59:59") 57 | # Add the value together with its key to the Exif data container 58 | key = exiv2.ExifKey("Exif.Photo.DateTimeOriginal") 59 | exifData.add(key, v) 60 | print('Added key "{}", value "{}"'.format(key, v)) 61 | 62 | # Now create a more interesting value (without using the create method) 63 | rv = exiv2.URationalValue() 64 | # Set two rational components from a string 65 | rv.read("1/2 1/3") 66 | # Add more elements directly 67 | rv.append((2, 3)) 68 | rv.append((3, 4)) 69 | # Add the key and value pair to the Exif data 70 | key = exiv2.ExifKey("Exif.Image.PrimaryChromaticities") 71 | exifData.add(key, rv) 72 | print('Added key "{}", value "{}"'.format(key, rv)) 73 | 74 | # ************************************************************** 75 | # Modify Exif data 76 | 77 | # Since we know that the metadatum exists (or we don't mind 78 | # creating a new tag if it doesn't), we can simply do this: 79 | tag = exifData["Exif.Photo.DateTimeOriginal"] 80 | date = tag.toString() 81 | date = '2000' + date[4:] 82 | tag.setValue(date) 83 | print('Modified key "{}", new value "{}"'.format( 84 | tag.key(), tag.value())) 85 | 86 | # Alternatively, we can use findKey() 87 | key = exiv2.ExifKey("Exif.Image.PrimaryChromaticities") 88 | pos = exifData.findKey(key) 89 | if pos == exifData.end(): 90 | raise exiv2.Exiv2Error("Key not found") 91 | # Get a copy of the value 92 | v = pos.getValue() 93 | # Downcast the Value pointer to its actual type 94 | rv = exiv2.URationalValue(v) 95 | # Modify the value directly 96 | rv[2] = 88, 77 97 | 98 | # ************************************************************** 99 | # Delete metadata from the Exif data container 100 | 101 | # Delete the metadatum at iterator position pos 102 | key = exiv2.ExifKey("Exif.Image.PrimaryChromaticities") 103 | pos = exifData.findKey(key) 104 | if pos == exifData.end(): 105 | raise exiv2.Exiv2Error("Key not found") 106 | exifData.erase(pos) 107 | print('Deleted key "{}"'.format(key)) 108 | 109 | # ************************************************************** 110 | # Finally, write the remaining Exif data to the image file 111 | image = exiv2.ImageFactory.open(file) 112 | 113 | image.setExifData(exifData) 114 | image.writeMetadata() 115 | 116 | return 0 117 | except exiv2.Exiv2Error as e: 118 | print('Caught Exiv2 exception "{}"'.format(str(e))) 119 | return -1; 120 | finally: 121 | exiv2.XmpParser.terminate() 122 | return 0 123 | 124 | if __name__ == "__main__": 125 | sys.exit(main()) 126 | -------------------------------------------------------------------------------- /examples/easyaccess.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program to show use of "EasyAccess API". 21 | # See https://github.com/Exiv2/exiv2/wiki/EasyAccess-API 22 | 23 | import locale 24 | import sys 25 | 26 | import exiv2 27 | 28 | 29 | def main(): 30 | locale.setlocale(locale.LC_ALL, '') 31 | exiv2.XmpParser.initialize() 32 | 33 | if len(sys.argv) != 2: 34 | print("Usage: {} file".format(sys.argv[0])) 35 | return 1 36 | 37 | file = sys.argv[1] 38 | 39 | image = exiv2.ImageFactory.open(file) 40 | image.readMetadata() 41 | exifData = image.exifData() 42 | 43 | for name in ('make', 'model', 'dateTimeOriginal', 'exposureTime', 44 | 'apertureValue', 'exposureBiasValue', 'exposureIndex', 'flash', 45 | 'flashBias', 'flashEnergy', 'focalLength', 'subjectDistance', 46 | 'isoSpeed', 'exposureMode', 'meteringMode', 'macroMode', 47 | 'imageQuality', 'whiteBalance', 'orientation', 'sceneMode', 48 | 'sceneCaptureType', 'lensName', 'saturation', 'sharpness', 49 | 'contrast', 'fNumber', 'serialNumber', 'afPoint', 50 | 'shutterSpeedValue', 'brightnessValue', 'maxApertureValue', 51 | 'lightSource', 'subjectArea', 'sensingMethod'): 52 | if hasattr(exiv2, name): 53 | datum = getattr(exiv2, name)(exifData) 54 | if datum: 55 | print('{:18s}: {:30s}: {:s}'.format( 56 | name, datum.key(), datum.print(exifData))) 57 | else: 58 | print(name) 59 | 60 | return 0 61 | 62 | 63 | if __name__ == "__main__": 64 | sys.exit(main()) 65 | -------------------------------------------------------------------------------- /examples/exifcomment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program showing how to set the Exif comment of an image. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | def main(): 28 | locale.setlocale(locale.LC_ALL, '') 29 | try: 30 | exiv2.XmpParser.initialize() 31 | if len(sys.argv) != 2: 32 | print('Usage: {} file'.format(sys.argv[0])) 33 | return 1; 34 | file = sys.argv[1] 35 | 36 | image = exiv2.ImageFactory.open(file) 37 | image.readMetadata() 38 | exifData = image.exifData() 39 | 40 | # Exiv2 uses a CommentValue for Exif user comments. The format 41 | # of the comment string includes an optional charset 42 | # specification at the beginning: 43 | 44 | # [charset=[Ascii|Jis|Unicode|Undefined]] comment 45 | 46 | # Undefined is used as a default if the comment doesn't start 47 | # with a charset definition. 48 | 49 | # Following are a few examples of valid comments. The last one 50 | # is written to the file. 51 | 52 | exifData["Exif.Photo.UserComment"] = ( 53 | "charset=Unicode A Unicode Exif comment added with Exiv2") 54 | exifData["Exif.Photo.UserComment"] = ( 55 | "charset=Undefined An undefined Exif comment added with Exiv2") 56 | exifData["Exif.Photo.UserComment"] = ( 57 | "Another undefined Exif comment added with Exiv2") 58 | exifData["Exif.Photo.UserComment"] = ( 59 | "charset=Ascii An ASCII Exif comment added with Exiv2") 60 | 61 | print("Writing user comment '{}' back to the image.".format( 62 | exifData["Exif.Photo.UserComment"])) 63 | 64 | image.writeMetadata() 65 | 66 | return 0 67 | except exiv2.Exiv2Error as e: 68 | print('Caught Exiv2 exception "{}"'.format(str(e))) 69 | return -1; 70 | finally: 71 | exiv2.XmpParser.terminate() 72 | return 0 73 | 74 | if __name__ == "__main__": 75 | sys.exit(main()) 76 | -------------------------------------------------------------------------------- /examples/exifdata.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program to format exif data in various external formats. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | 28 | def syntax(argv, formats): 29 | print("Usage: {} file format".format(argv[0])) 30 | print("formats: {}".format(' | '.join(formats))) 31 | 32 | 33 | def formatJSON(exifData): 34 | count = 0 35 | length = exifData.count() 36 | print('{') 37 | for datum in exifData: 38 | count += 1 39 | print(' "{}":"{}"{}'.format( 40 | datum.key(), datum.print(exifData).replace('"', r'\"'), 41 | ('',',')[count != length])) 42 | print('}') 43 | 44 | 45 | def formatXML(exifData): 46 | count = 0 47 | length = exifData.count() 48 | print('') 49 | for datum in exifData: 50 | count += 1 51 | print(' <{key}>{value}<{key}/>'.format( 52 | key=datum.key(), 53 | value=datum.print(exifData).replace( 54 | '<', '<').replace('>', '>'))) 55 | print('') 56 | 57 | 58 | def main(): 59 | locale.setlocale(locale.LC_ALL, '') 60 | try: 61 | exiv2.XmpParser.initialize() 62 | 63 | formats = { 64 | "json": formatJSON, 65 | "xml": formatXML, 66 | } 67 | 68 | if len(sys.argv) != 3: 69 | syntax(sys.argv, formats) 70 | return 1; 71 | file = sys.argv[1] 72 | format_ = sys.argv[2] 73 | 74 | if format_ not in formats: 75 | print("Unrecognised format {}".format(format_)) 76 | syntax(sys.argv, formats) 77 | return 2; 78 | 79 | image = exiv2.ImageFactory.open(file) 80 | image.readMetadata() 81 | exifData = image.exifData() 82 | 83 | formats[format_](exifData) 84 | 85 | return 0 86 | except exiv2.Exiv2Error as e: 87 | print('*** error exiv2 exception "{}" ***'.format(str(e))) 88 | return 4; 89 | except Exception as e: 90 | print('*** error exception "{}" ***'.format(str(e))) 91 | return 5; 92 | finally: 93 | exiv2.XmpParser.terminate() 94 | return 0 95 | 96 | if __name__ == "__main__": 97 | sys.exit(main()) 98 | -------------------------------------------------------------------------------- /examples/exifprint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program to print Exif data from an image. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | 28 | def main(): 29 | locale.setlocale(locale.LC_ALL, '') 30 | try: 31 | exiv2.XmpParser.initialize() 32 | 33 | if len(sys.argv) != 2: 34 | print("Usage: {} [ path | --version | --version-test ]".format( 35 | sys.argv[0])) 36 | return 1; 37 | file = sys.argv[1] 38 | 39 | if file == '--version': 40 | # Python interface won't build dumpLibraryInfo 41 | ## Exiv2::dumpLibraryInfo(std::cout, keys) 42 | print(exiv2.versionString()) 43 | return 0; 44 | 45 | if file == '--version-test': 46 | # verifies/test macro EXIV2_TEST_VERSION 47 | # described in include/exiv2/version.hpp 48 | # Python interface doesn't include C++ macros! 49 | ## print("EXV_PACKAGE_VERSION ", EXV_PACKAGE_VERSION) 50 | print("Exiv2::version() ", exiv2.version()) 51 | print("strlen(Exiv2::version()) ", len(exiv2.version())) 52 | print("Exiv2::versionNumber() ", exiv2.versionNumber()) 53 | print("Exiv2::versionString() ", exiv2.versionString()) 54 | print("Exiv2::versionNumberHexString() ", 55 | exiv2.versionNumberHexString()) 56 | 57 | # Test the Exiv2 version available at runtime but compile 58 | # the if-clause only if the compile-time version is at least 59 | # 0.15. Earlier versions didn't have a testVersion() 60 | # function: 61 | if hasattr(exiv2, 'testVersion'): 62 | if exiv2.testVersion(0,13,0): 63 | print("Available Exiv2 version is equal to or greater" 64 | " than 0.13") 65 | else: 66 | print("Installed Exiv2 version is less than 0.13") 67 | else: 68 | print("Compile-time Exiv2 version doesn't have" 69 | " exiv2.testVersion()") 70 | return 0 71 | 72 | image = exiv2.ImageFactory.open(file) 73 | image.readMetadata() 74 | exifData = image.exifData() 75 | if exifData.empty(): 76 | raise exiv2.Exiv2Error("No Exif data found in file") 77 | 78 | end = exifData.end() 79 | i = exifData.begin() 80 | while i != end: 81 | print('{:44s} {:04x} {:9s} {:3d} {:s}'.format( 82 | i.key(), i.tag(), i.typeName(), i.count(), i.toString())) 83 | next(i) 84 | 85 | return 0 86 | except exiv2.Exiv2Error as e: 87 | print('*** error exiv2 exception "{}" ***'.format(str(e))) 88 | return 4; 89 | except Exception as e: 90 | print('*** error exception "{}" ***'.format(str(e))) 91 | return 5; 92 | finally: 93 | exiv2.XmpParser.terminate() 94 | return 0 95 | 96 | if __name__ == "__main__": 97 | sys.exit(main()) 98 | -------------------------------------------------------------------------------- /examples/exifvalue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program to print value of an exif key in an image. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | 28 | def main(): 29 | locale.setlocale(locale.LC_ALL, '') 30 | try: 31 | exiv2.XmpParser.initialize() 32 | 33 | if len(sys.argv) != 3: 34 | print("Usage: {} file key".format(sys.argv[0])) 35 | return 1 36 | 37 | file = sys.argv[1] 38 | key = sys.argv[2] 39 | 40 | image = exiv2.ImageFactory.open(file) 41 | image.readMetadata() 42 | exifData = image.exifData() 43 | if exifData.empty(): 44 | print("No metadata found in file", file) 45 | return 2 46 | 47 | print(exifData[key]) 48 | 49 | return 0 50 | except exiv2.Exiv2Error as e: 51 | print('*** error exiv2 exception "{}" ***'.format(str(e))) 52 | return 4; 53 | except Exception as e: 54 | print('*** error exception "{}" ***'.format(str(e))) 55 | return 5; 56 | finally: 57 | exiv2.XmpParser.terminate() 58 | return 0 59 | 60 | if __name__ == "__main__": 61 | sys.exit(main()) 62 | -------------------------------------------------------------------------------- /examples/iptceasy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # The quickest way to access, set or modify IPTC metadata. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | def main(): 28 | locale.setlocale(locale.LC_ALL, '') 29 | try: 30 | exiv2.XmpParser.initialize() 31 | if len(sys.argv) != 2: 32 | print('Usage: {} file'.format(sys.argv[0])) 33 | return 1; 34 | file = sys.argv[1] 35 | 36 | iptcData = exiv2.IptcData() 37 | 38 | iptcData["Iptc.Application2.Headline"] = "The headline I am" 39 | iptcData["Iptc.Application2.Keywords"] = "Yet another keyword" 40 | iptcData["Iptc.Application2.DateCreated"] = "2004-08-03" 41 | iptcData["Iptc.Application2.Urgency"] = exiv2.UShortValue(1) 42 | iptcData["Iptc.Envelope.ModelVersion"] = 42 43 | iptcData["Iptc.Envelope.TimeSent"] = "14:41:00-05:00" 44 | iptcData["Iptc.Application2.RasterizedCaption"] = "230 42 34 2 90 84 23 146" 45 | iptcData["Iptc.0x0009.0x0001"] = "Who am I?" 46 | 47 | value = exiv2.StringValue() 48 | value.read("very!") 49 | iptcData["Iptc.Application2.Urgency"] = value 50 | 51 | print("Time sent: {}".format(iptcData["Iptc.Envelope.TimeSent"])) 52 | 53 | # Open image file 54 | image = exiv2.ImageFactory.open(file) 55 | 56 | # Set IPTC data and write it to the file 57 | image.setIptcData(iptcData) 58 | image.writeMetadata() 59 | 60 | return 0 61 | except exiv2.Exiv2Error as e: 62 | print('Caught Exiv2 exception "{}"'.format(str(e))) 63 | return -1; 64 | finally: 65 | exiv2.XmpParser.terminate() 66 | return 0 67 | 68 | if __name__ == "__main__": 69 | sys.exit(main()) 70 | -------------------------------------------------------------------------------- /examples/thumbnail.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample program to display an image's Exif thumbnail. 21 | 22 | import io 23 | import locale 24 | import sys 25 | 26 | import PIL.Image as PIL 27 | from PIL import ImageShow 28 | import exiv2 29 | 30 | 31 | # Make sure we use ImageMagick viewer if it's available 32 | ImageShow.register(ImageShow.DisplayViewer(), -1) 33 | 34 | 35 | def main(): 36 | locale.setlocale(locale.LC_ALL, '') 37 | try: 38 | exiv2.XmpParser.initialize() 39 | 40 | if len(sys.argv) != 2: 41 | print("Usage: {} file".format(sys.argv[0])) 42 | return 1 43 | 44 | file = sys.argv[1] 45 | 46 | image = exiv2.ImageFactory.open(file) 47 | image.readMetadata() 48 | exifData = image.exifData() 49 | 50 | thumb = exiv2.ExifThumb(exifData) 51 | print('Thumbnail type:', thumb.mimeType()) 52 | 53 | data = thumb.copy() 54 | if not data: 55 | print("Image has no thumbnail data") 56 | return -1; 57 | 58 | print('Thumbnail data:', bytes(data.data())[:8], 59 | '...', bytes(data.data())[-8:]) 60 | 61 | print("Displaying thumbnail image") 62 | thumb_image = PIL.open(io.BytesIO(data.data())) 63 | thumb_image.show() 64 | 65 | return 0 66 | except exiv2.Exiv2Error as e: 67 | print('*** error exiv2 exception "{}" ***'.format(str(e))) 68 | return 4; 69 | except Exception as e: 70 | print('*** error exception "{}" ***'.format(str(e))) 71 | return 5; 72 | finally: 73 | exiv2.XmpParser.terminate() 74 | return 0 75 | 76 | if __name__ == "__main__": 77 | sys.exit(main()) 78 | -------------------------------------------------------------------------------- /examples/values.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-22 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | 21 | # Accessing some Exiv2::Value types directly, without going via string 22 | # representations. For most purposes, using string representations of 23 | # values is good enough, but for maximum speed and precision you can use 24 | # more appropriate Python types. 25 | 26 | import datetime 27 | from fractions import Fraction 28 | import locale 29 | import sys 30 | 31 | import exiv2 32 | 33 | def main(): 34 | locale.setlocale(locale.LC_ALL, '') 35 | print('==== DateValue & TimeValue ====') 36 | # These are only used in Iptc - Exif & Xmp use string representations. 37 | tz = datetime.timezone(datetime.timedelta(hours=2, minutes=45)) 38 | py_datetime = datetime.datetime.now(tz).replace(microsecond=0) 39 | print("Python datetime:", py_datetime) 40 | # Python -> Exiv2 41 | # can pass value to constructor or use setDate() afterwards 42 | date = exiv2.DateValue(py_datetime.year, py_datetime.month, py_datetime.day) 43 | print("Exiv2 date:", date) 44 | tz_minute = int(py_datetime.utcoffset().total_seconds()) // 60 45 | tz_hour = tz_minute // 60 46 | tz_minute -= tz_hour * 60 47 | time = exiv2.TimeValue(py_datetime.hour, py_datetime.minute, 48 | py_datetime.second, tz_hour, tz_minute) 49 | print("Exiv2 time:", time) 50 | # Exiv2 -> Python 51 | date_st = date.getDate() 52 | time_st = time.getTime() 53 | tz_info = datetime.timezone(datetime.timedelta( 54 | hours=time_st.tzHour, minutes=time_st.tzMinute)) 55 | py_datetime = datetime.datetime( 56 | **date_st, hour=time_st.hour, minute=time_st.minute, 57 | second=time_st.second, tzinfo=tz_info); 58 | print("Python datetime:", py_datetime) 59 | 60 | print('==== ShortValue ====') 61 | # This stores 1 or more 16-bit ints. 62 | py_shorts = [34, 56, 78] 63 | print("Python short:", py_shorts) 64 | # Python -> Exiv2, the long way 65 | shorts = exiv2.ShortValue() 66 | # append values to initialise 67 | for x in py_shorts: 68 | shorts.append(x) 69 | print("Exiv2 short:", shorts) 70 | # Python -> Exiv2, the short way 71 | shorts = exiv2.ShortValue(py_shorts) 72 | print("Exiv2 short:", shorts) 73 | # modify a value by index 74 | shorts[1] = 12 75 | print("Exiv2 short:", shorts) 76 | # append a value 77 | shorts.append(90) 78 | print("Exiv2 short:", shorts) 79 | # Exiv2 -> Python 80 | py_shorts = list(shorts) 81 | print("Python short:", py_shorts) 82 | 83 | print('==== RationalValue ====') 84 | # This stores 1 or more pairs of ints. Typical usage is 85 | # Exif.GPSInfo.GPSLatitude, which stores degrees, minutes & seconds. 86 | py_latitude = [Fraction(51), Fraction(30), 87 | Fraction(4.6).limit_denominator(100000)] 88 | print("Python rational:", py_latitude) 89 | # Python -> Exiv2, the long way 90 | latitude = exiv2.RationalValue() 91 | # append values to initialise 92 | for x in py_latitude: 93 | latitude.append((x.numerator, x.denominator)) 94 | print("Exiv2 rational:", latitude) 95 | # Python -> Exiv2, the short way 96 | latitude = exiv2.RationalValue( 97 | [(x.numerator, x.denominator) for x in py_latitude]) 98 | print("Exiv2 rational:", latitude) 99 | # modify a value by index 100 | latitude[1] = -63, 11 101 | print("Exiv2 rational:", latitude) 102 | # append a value 103 | latitude.append((19, 3)) 104 | print("Exiv2 rational:", latitude) 105 | # Exiv2 -> Python 106 | py_latitude = [Fraction(*x) for x in latitude] 107 | print("Python rational:", py_latitude) 108 | 109 | print('==== XmpArrayValue ====') 110 | # This is used for XmpSeq, XmpBag and XmpAlt values. It stores one 111 | # or more strings. 112 | py_seq = ['First string', 'Second', 'Third'] 113 | print("Python seq:", py_seq) 114 | # Python -> Exiv2, the long way 115 | seq = exiv2.XmpArrayValue(exiv2.TypeId.xmpBag) 116 | # append values to initialise 117 | for x in py_seq: 118 | seq.append(x) 119 | print("Exiv2 seq:", seq) 120 | # Python -> Exiv2, the short way 121 | seq = exiv2.XmpArrayValue(py_seq, exiv2.TypeId.xmpBag) 122 | print("Exiv2 seq:", seq) 123 | # Exiv2 -> Python 124 | py_seq = list(seq) 125 | print("Python seq:", py_seq) 126 | 127 | print('==== LangAltValue ====') 128 | # Used to store text with default language and other language alternatives 129 | py_langalt = {'x-default': 'default', 'de': 'Deutsch', 'fr': 'French'} 130 | print("Python langalt:", py_langalt) 131 | # Python -> Exiv2, the long way 132 | langalt = exiv2.LangAltValue() 133 | for key in py_langalt: 134 | langalt[key] = py_langalt[key] 135 | print("Exiv2 langalt:", langalt) 136 | # Python -> Exiv2, the short way 137 | langalt = exiv2.LangAltValue(py_langalt) 138 | print("Exiv2 langalt:", langalt) 139 | print('keys', langalt.keys()) 140 | print('values', langalt.values()) 141 | print('items', langalt.items()) 142 | # delete a value 143 | del langalt['fr'] 144 | # add a value 145 | langalt['nl'] = 'Nederlands' 146 | # Exiv2 -> Python 147 | py_langalt = dict(langalt) 148 | print("Python langalt:", py_langalt) 149 | 150 | print('==== DataValue ====') 151 | py_data = b'0123456789' 152 | print("Python data:", py_data) 153 | # Python -> Exiv2 154 | data = exiv2.DataValue(py_data) 155 | print("Exiv2 data:", data) 156 | # Exiv2 -> Python 157 | py_data = bytearray(len(data)) 158 | data.copy(py_data) 159 | print("Python data:", py_data) 160 | 161 | return 0 162 | 163 | 164 | if __name__ == "__main__": 165 | sys.exit(main()) 166 | -------------------------------------------------------------------------------- /examples/xmpsample.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # python-exiv2 - Python interface to libexiv2 4 | # http://github.com/jim-easterbrook/python-exiv2 5 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | # Sample/test for high level XMP classes. See also addmoddel.py. 21 | 22 | import locale 23 | import sys 24 | 25 | import exiv2 26 | 27 | def main(): 28 | locale.setlocale(locale.LC_ALL, '') 29 | try: 30 | exiv2.XmpParser.initialize() 31 | 32 | # The XMP property container 33 | xmpData = exiv2.XmpData() 34 | 35 | # -------------------------------------------------------------- 36 | # Teaser: Setting XMP properties doesn't get much easier than 37 | # this: 38 | 39 | xmpData["Xmp.dc.source"] = "xmpsample.cpp" # a simple text value 40 | xmpData["Xmp.dc.subject"] = "Palmtree" # an array item 41 | xmpData["Xmp.dc.subject"] = "Rubbertree" # add a 2nd array item 42 | # a language alternative with two entries and without default 43 | xmpData["Xmp.dc.title"] = "lang=de-DE Sonnenuntergang am Strand" 44 | xmpData["Xmp.dc.title"] = "lang=en-US Sunset on the beach" 45 | 46 | # -------------------------------------------------------------- 47 | # Any properties can be set provided the namespace is known. 48 | # Values of any type can be assigned to an Xmpdatum, if they 49 | # have an output operator. The default XMP value type for 50 | # unknown properties is a simple text value. 51 | 52 | xmpData["Xmp.dc.one"] = '-1' 53 | xmpData["Xmp.dc.two"] = '3.1415' 54 | xmpData["Xmp.dc.three"] = exiv2.RationalValue((5, 7)) 55 | xmpData["Xmp.dc.four"] = exiv2.UShortValue(255) 56 | xmpData["Xmp.dc.five"] = '256' 57 | xmpData["Xmp.dc.six"] = 'False' 58 | 59 | except exiv2.Exiv2Error as e: 60 | print('Caught Exiv2 exception "{}"'.format(str(e))) 61 | return -1; 62 | finally: 63 | exiv2.XmpParser.terminate() 64 | return 0 65 | 66 | if __name__ == "__main__": 67 | sys.exit(main()) 68 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools >= 59.6", "toml"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "exiv2" 7 | description = "Python interface to libexiv2" 8 | readme = "README.rst" 9 | license = {text = "GNU GPL"} 10 | authors = [ 11 | {name = "Jim Easterbrook", email = "jim@jim-easterbrook.me.uk"} 12 | ] 13 | classifiers = [ 14 | "Development Status :: 4 - Beta", 15 | "Intended Audience :: Developers", 16 | "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", 17 | "Operating System :: MacOS", 18 | "Operating System :: MacOS :: MacOS X", 19 | "Operating System :: POSIX", 20 | "Operating System :: POSIX :: Linux", 21 | "Operating System :: Microsoft", 22 | "Operating System :: Microsoft :: Windows", 23 | "Programming Language :: Python :: 3", 24 | "Topic :: Multimedia", 25 | "Topic :: Multimedia :: Graphics", 26 | ] 27 | dynamic = ["version"] 28 | 29 | [project.urls] 30 | Homepage = "https://github.com/jim-easterbrook/python-exiv2" 31 | Changelog = "https://github.com/jim-easterbrook/python-exiv2/blob/main/CHANGELOG.txt" 32 | Documentation = "https://python-exiv2.readthedocs.io/" 33 | 34 | [tool.setuptools] 35 | platforms = ["POSIX", "MacOS", "Windows"] 36 | zip-safe = false 37 | 38 | [tool.setuptools.dynamic] 39 | version = {attr = "exiv2.__version__"} 40 | -------------------------------------------------------------------------------- /src/doc/_templates/module.rst: -------------------------------------------------------------------------------- 1 | {% if fullname == "exiv2._version" %} 2 | {% set attributes = ['__version__', '__version_tuple__'] %} 3 | {% endif %} 4 | 5 | {% extends "!autosummary/module.rst" %} 6 | 7 | {% block classes %} 8 | {% if classes %} 9 | .. rubric:: {{ _('Classes') }} 10 | 11 | {% if fullname == "exiv2._value" %} 12 | .. inheritance-diagram:: {{ classes | 13 | reject("in", ["Date", "Time"]) | 14 | join(" ") }} 15 | :top-classes: exiv2.value.Value 16 | {% endif %} 17 | 18 | {% if fullname in ["exiv2._datasets", "exiv2._metadatum", "exiv2._properties", "exiv2._tags"] %} 19 | .. inheritance-diagram:: exiv2.ExifKey exiv2.IptcKey exiv2.XmpKey 20 | :top-classes: exiv2.metadatum.Key 21 | {% endif %} 22 | 23 | {% if fullname in ["exiv2._exif", "exiv2._iptc", "exiv2._metadatum", "exiv2._xmp"] %} 24 | .. inheritance-diagram:: exiv2.Exifdatum exiv2.Iptcdatum exiv2.Xmpdatum 25 | :top-classes: exiv2.metadatum.Metadatum 26 | {% endif %} 27 | 28 | .. autosummary:: 29 | {% for item in classes %} 30 | {{ item }} 31 | {%- endfor %} 32 | {% endif %} 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /src/doc/index.rst: -------------------------------------------------------------------------------- 1 | .. This is part of the python-exiv2 documentation. 2 | Copyright (C) 2024 Jim Easterbrook. 3 | 4 | Python-exiv2 documentation 5 | ========================== 6 | 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | misc/readme 12 | misc/install 13 | misc/usage 14 | misc/changelog 15 | misc/api 16 | 17 | -------------------------------------------------------------------------------- /src/doc/misc/api.rst: -------------------------------------------------------------------------------- 1 | .. This is part of the python-exiv2 documentation. 2 | Copyright (C) 2024 Jim Easterbrook. 3 | 4 | Detailed API 5 | ============ 6 | 7 | This part of the documentation is auto-generated from the Doxygen_ format documentation in the libexiv2 "header" files. 8 | There are many ways in which the conversion process can fail, so you may need to consult the `Exiv2 C++ API`_ documentation as well. 9 | 10 | The documentation is split into several pages, one for each module in the Python interface. 11 | This makes it easier to use than having all the classes and functions in one document. 12 | Do not use the module names in your Python scripts: always use ``exiv2.name`` rather than ``exiv2.module.name`` or ``exiv2._module.name``. 13 | 14 | See :ref:`genindex` for a full index to all classes, attributes, functions and methods. 15 | 16 | .. autosummary:: 17 | :toctree: ../api 18 | :recursive: 19 | :template: module.rst 20 | 21 | exiv2._image 22 | exiv2._exif 23 | exiv2._iptc 24 | exiv2._xmp 25 | exiv2._preview 26 | exiv2._value 27 | exiv2._types 28 | exiv2._tags 29 | exiv2._datasets 30 | exiv2._properties 31 | exiv2._version 32 | exiv2._error 33 | exiv2._easyaccess 34 | exiv2._basicio 35 | exiv2._metadatum 36 | 37 | .. _Doxygen: https://www.doxygen.nl/ 38 | .. _Exiv2 C++ API: https://exiv2.org/doc/index.html 39 | -------------------------------------------------------------------------------- /src/doc/misc/changelog.rst: -------------------------------------------------------------------------------- 1 | .. This is part of the python-exiv2 documentation. 2 | Copyright (C) 2024 Jim Easterbrook. 3 | 4 | Release history 5 | =============== 6 | 7 | .. literalinclude:: ../../../CHANGELOG.txt 8 | :language: none 9 | :start-after: licenses/> 10 | :end-before: Changes in v0.4.0 11 | -------------------------------------------------------------------------------- /src/doc/misc/install.rst: -------------------------------------------------------------------------------- 1 | .. This is part of the python-exiv2 documentation. 2 | Copyright (C) 2024 Jim Easterbrook. 3 | 4 | .. include:: ../../../INSTALL.rst 5 | :end-before: .. _README 6 | 7 | .. _README.rst: readme.html 8 | -------------------------------------------------------------------------------- /src/doc/misc/readme.rst: -------------------------------------------------------------------------------- 1 | .. This is part of the python-exiv2 documentation. 2 | Copyright (C) 2024 Jim Easterbrook. 3 | 4 | Project overview 5 | ================ 6 | 7 | .. include:: ../../../README.rst 8 | :start-line: 2 9 | :end-before: .. _INSTALL 10 | 11 | .. _INSTALL.rst: install.html 12 | .. _USAGE.rst: usage.html 13 | -------------------------------------------------------------------------------- /src/doc/misc/usage.rst: -------------------------------------------------------------------------------- 1 | .. This is part of the python-exiv2 documentation. 2 | Copyright (C) 2024 Jim Easterbrook. 3 | 4 | .. include:: ../../../USAGE.rst 5 | -------------------------------------------------------------------------------- /src/doc/requirements.txt: -------------------------------------------------------------------------------- 1 | exiv2 <= 0.17.3 2 | sphinx == 7.2.6 3 | sphinx-rtd-theme == 2.0.0 4 | -------------------------------------------------------------------------------- /src/interface/__main__.py: -------------------------------------------------------------------------------- 1 | # python-exiv2 - Python interface to exiv2 2 | # http://github.com/jim-easterbrook/python-exiv2 3 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import argparse 19 | import os 20 | import pprint 21 | import sys 22 | 23 | import exiv2 24 | 25 | def main(): 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument('-v', '--verbosity', help='increase output verbosity', 28 | action='store_true') 29 | args = parser.parse_args() 30 | print('libexiv2 version:', exiv2.version()) 31 | print('python-exiv2 version:', exiv2.__version__) 32 | print('python-exiv2 examples:', 33 | os.path.join(os.path.dirname(__file__), 'examples')) 34 | if args.verbosity: 35 | print('libexiv2 build options:') 36 | pprint.pprint(exiv2.versionInfo()) 37 | 38 | if __name__ == "__main__": 39 | sys.exit(main()) 40 | -------------------------------------------------------------------------------- /src/interface/datasets.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") datasets 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "IPTC key class and data attributes."; 22 | #endif 23 | 24 | %include "shared/preamble.i" 25 | %include "shared/enum.i" 26 | %include "shared/exception.i" 27 | %include "shared/static_list.i" 28 | %include "shared/struct_dict.i" 29 | %include "shared/unique_ptr.i" 30 | 31 | %import "metadatum.i" 32 | 33 | IMPORT_ENUM(TypeId) 34 | 35 | // Catch some C++ exceptions 36 | %exception; 37 | EXCEPTION(Exiv2::IptcDataSets::dataSet) 38 | EXCEPTION(Exiv2::IptcDataSets::recordId) 39 | EXCEPTION(Exiv2::IptcKey::IptcKey(std::string)) 40 | EXCEPTION(Exiv2::IptcKey::IptcKey(std::string const &)) 41 | 42 | EXTEND_KEY(Exiv2::IptcKey); 43 | 44 | // IptcDataSets::application2RecordList and IptcDataSets::envelopeRecordList 45 | // return a static list as a pointer 46 | LIST_POINTER(const Exiv2::DataSet*, Exiv2::DataSet, number_ != 0xffff) 47 | 48 | // Give Exiv2::DataSet dict-like behaviour 49 | STRUCT_DICT(Exiv2::DataSet) 50 | 51 | // Structs are all static data 52 | %ignore Exiv2::IptcDataSets::IptcDataSets; 53 | %ignore Exiv2::IptcDataSets::~IptcDataSets; 54 | %ignore Exiv2::DataSet::DataSet; 55 | %ignore Exiv2::DataSet::~DataSet; 56 | 57 | // Ignore stuff that Python can't use or doesn't need 58 | %ignore Exiv2::Dictionary; 59 | %ignore Exiv2::Dictionary_i; 60 | %ignore Exiv2::IptcDataSets::dataSetList; 61 | %ignore Exiv2::RecordInfo; 62 | %ignore Exiv2::StringSet; 63 | %ignore Exiv2::StringSet_i; 64 | %ignore Exiv2::StringVector; 65 | %ignore Exiv2::StringVector_i; 66 | %ignore Exiv2::Uint32Vector; 67 | %ignore Exiv2::Uint32Vector_i; 68 | 69 | %immutable; 70 | %include "exiv2/datasets.hpp" 71 | %mutable; 72 | -------------------------------------------------------------------------------- /src/interface/easyaccess.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") easyaccess 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "Simplified reading of Exif metadata."; 22 | #endif 23 | 24 | %include "shared/preamble.i" 25 | %include "shared/exception.i" 26 | %include "shared/exv_options.i" 27 | 28 | // Catch all C++ exceptions 29 | EXCEPTION() 30 | 31 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::apertureValue) 32 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::brightnessValue) 33 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::dateTimeOriginal) 34 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::exposureBiasValue) 35 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::exposureIndex) 36 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::flash) 37 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::flashEnergy) 38 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::lightSource) 39 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::maxApertureValue) 40 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::sensingMethod) 41 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::shutterSpeedValue) 42 | EXV_ENABLE_EASYACCESS_FUNCTION(Exiv2::subjectArea) 43 | 44 | // Store data.end() after converting input 45 | %typemap(check) Exiv2::ExifData& (Exiv2::ExifData::const_iterator _global_end) %{ 46 | _global_end = $1->end(); 47 | %} 48 | 49 | // Convert result from iterator to datum or None 50 | %typemap(out) Exiv2::ExifData::const_iterator %{ 51 | if ($1 == _global_end) 52 | $result = SWIG_Py_Void(); 53 | else 54 | $result = SWIG_NewPointerObj( 55 | SWIG_as_voidptr(&(*$1)), $descriptor(Exiv2::Exifdatum*), 0); 56 | %} 57 | 58 | %include "exiv2/easyaccess.hpp" 59 | -------------------------------------------------------------------------------- /src/interface/exif.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") exif 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "Exif metadatum, container and iterators."; 22 | #endif 23 | 24 | #pragma SWIG nowarn=508 // Declaration of '__str__' shadows declaration accessible via operator->() 25 | 26 | %include "shared/preamble.i" 27 | %include "shared/buffers.i" 28 | %include "shared/containers.i" 29 | %include "shared/data_iterator.i" 30 | %include "shared/enum.i" 31 | %include "shared/exception.i" 32 | %include "shared/exv_options.i" 33 | %include "shared/keep_reference.i" 34 | %include "shared/windows_path.i" 35 | 36 | %include "stdint.i" 37 | %include "std_string.i" 38 | 39 | %import "tags.i" 40 | 41 | IMPORT_ENUM(ByteOrder) 42 | IMPORT_ENUM(TypeId) 43 | 44 | // Catch all C++ exceptions 45 | EXCEPTION() 46 | 47 | EXV_ENABLE_FILESYSTEM_FUNCTION(Exiv2::ExifThumb::setJpegThumbnail( 48 | const std::string&)) 49 | EXV_ENABLE_FILESYSTEM_FUNCTION(Exiv2::ExifThumb::setJpegThumbnail( 50 | const std::string&, URational, URational, uint16_t)) 51 | EXV_ENABLE_FILESYSTEM_FUNCTION(Exiv2::ExifThumbC::writeFile) 52 | 53 | // ExifThumb keeps a reference to the ExifData it uses 54 | KEEP_REFERENCE_EX(Exiv2::ExifThumb*, args) 55 | 56 | #if EXIV2_VERSION_HEX < 0x001c0000 57 | INPUT_BUFFER_RO(const Exiv2::byte* buf, long size) 58 | #else 59 | INPUT_BUFFER_RO(const Exiv2::byte* buf, size_t size) 60 | #endif 61 | 62 | EXTEND_METADATUM(Exiv2::Exifdatum) 63 | 64 | DATA_ITERATOR_TYPEMAPS(ExifData) 65 | #ifndef SWIGIMPORTED 66 | DATA_ITERATOR_CLASSES(ExifData, Exifdatum) 67 | #endif 68 | 69 | // Get the current (or default if not set) type id of a datum 70 | %fragment("get_type_id"{Exiv2::Exifdatum}, "header") { 71 | static Exiv2::TypeId get_type_id(Exiv2::Exifdatum* datum) { 72 | Exiv2::TypeId type_id = datum->typeId(); 73 | if (type_id != Exiv2::invalidTypeId) 74 | return type_id; 75 | return Exiv2::ExifKey(datum->key()).defaultTypeId(); 76 | }; 77 | } 78 | 79 | DATA_CONTAINER(Exiv2::ExifData, Exiv2::Exifdatum, Exiv2::ExifKey) 80 | 81 | // Convert path encoding on Windows 82 | WINDOWS_PATH(const std::string& path) 83 | 84 | // Ignore const overloads of some methods 85 | %ignore Exiv2::ExifData::operator[]; 86 | %ignore Exiv2::ExifData::begin() const; 87 | %ignore Exiv2::ExifData::end() const; 88 | %ignore Exiv2::ExifData::findKey(ExifKey const &) const; 89 | %ignore Exiv2::ExifParser; 90 | 91 | // Exifdatum::ifdId is documented as internal use only 92 | %ignore Exiv2::Exifdatum::ifdId; 93 | 94 | %include "exiv2/exif.hpp" 95 | -------------------------------------------------------------------------------- /src/interface/iptc.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") iptc 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "IPTC metadatum, container and iterators."; 22 | #endif 23 | 24 | #pragma SWIG nowarn=508 // Declaration of '__str__' shadows declaration accessible via operator->() 25 | 26 | %include "shared/preamble.i" 27 | %include "shared/containers.i" 28 | %include "shared/data_iterator.i" 29 | %include "shared/enum.i" 30 | %include "shared/exception.i" 31 | 32 | %include "stdint.i" 33 | %include "std_string.i" 34 | 35 | %import "datasets.i" 36 | 37 | IMPORT_ENUM(ByteOrder) 38 | IMPORT_ENUM(TypeId) 39 | 40 | // Catch all C++ exceptions 41 | EXCEPTION() 42 | 43 | EXTEND_METADATUM(Exiv2::Iptcdatum) 44 | 45 | DATA_ITERATOR_TYPEMAPS(IptcData) 46 | #ifndef SWIGIMPORTED 47 | DATA_ITERATOR_CLASSES(IptcData, Iptcdatum) 48 | #endif 49 | 50 | // Get the current (or default if not set) type id of a datum 51 | %fragment("get_type_id"{Exiv2::Iptcdatum}, "header") { 52 | static Exiv2::TypeId get_type_id(Exiv2::Iptcdatum* datum) { 53 | Exiv2::TypeId type_id = datum->typeId(); 54 | if (type_id != Exiv2::invalidTypeId) 55 | return type_id; 56 | return Exiv2::IptcDataSets::dataSetType(datum->tag(), datum->record()); 57 | }; 58 | } 59 | 60 | DATA_CONTAINER(Exiv2::IptcData, Exiv2::Iptcdatum, Exiv2::IptcKey) 61 | 62 | // Ignore const overloads of some methods 63 | %ignore Exiv2::IptcData::operator[]; 64 | %ignore Exiv2::IptcData::begin() const; 65 | %ignore Exiv2::IptcData::end() const; 66 | %ignore Exiv2::IptcData::findKey(IptcKey const &) const; 67 | %ignore Exiv2::IptcData::findId(uint16_t) const; 68 | %ignore Exiv2::IptcData::findId(uint16_t,uint16_t) const; 69 | %ignore Exiv2::IptcData::printStructure; 70 | %ignore Exiv2::IptcParser; 71 | 72 | %include "exiv2/iptc.hpp" 73 | -------------------------------------------------------------------------------- /src/interface/metadatum.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") metadatum 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "Exiv2 metadatum base class."; 22 | #endif 23 | 24 | %namewarn("") "print"; // don't rename print methods 25 | 26 | %include "shared/preamble.i" 27 | %include "shared/containers.i" 28 | %include "shared/exception.i" 29 | %include "shared/keep_reference.i" 30 | %include "shared/unique_ptr.i" 31 | 32 | %import "value.i" 33 | 34 | // Catch all C++ exceptions 35 | EXCEPTION() 36 | 37 | // Use default parameter for toFloat etc. 38 | %typemap(default) long n, size_t n {$1 = 0;} 39 | %ignore Exiv2::Metadatum::toFloat() const; 40 | %ignore Exiv2::Metadatum::toInt64() const; 41 | %ignore Exiv2::Metadatum::toLong() const; 42 | %ignore Exiv2::Metadatum::toRational() const; 43 | %ignore Exiv2::Metadatum::toUint32() const; 44 | 45 | // Use default parameter in print() and write() 46 | %typemap(default) const Exiv2::ExifData* pMetadata {$1 = NULL;} 47 | %ignore Exiv2::Metadatum::print() const; 48 | %ignore Exiv2::Metadatum::write(std::ostream &) const; 49 | 50 | %define EXTEND_KEY(key_type) 51 | UNIQUE_PTR(key_type); 52 | %feature("python:slot", "tp_str", functype="reprfunc") key_type::key; 53 | %enddef // EXTEND_KEY 54 | 55 | EXTEND_KEY(Exiv2::Key); 56 | 57 | // Macro for Metadatum subclasses 58 | %define EXTEND_METADATUM(datum_type) 59 | // Ignore overloaded default parameter version 60 | %ignore datum_type::write(std::ostream &) const; 61 | // Turn off exception checking for methods that are guaranteed not to throw 62 | %noexception datum_type::count; 63 | %noexception datum_type::size; 64 | // Keep a reference to Metadatum when calling value() 65 | KEEP_REFERENCE(const Exiv2::Value&) 66 | // Keep a reference to any object that returns a reference to a datum. 67 | KEEP_REFERENCE(datum_type&) 68 | // Set the datum's value from a Python object. The datum's current or default 69 | // type is used to create an Exiv2::Value object (via Python) from the Python 70 | // object. 71 | %fragment("set_value_from_py"{datum_type}, "header", 72 | fragment="get_type_object", fragment="get_type_id"{datum_type}) { 73 | static PyObject* set_value_from_py(datum_type* datum, PyObject* py_value) { 74 | swig_type_info* ty_info = get_type_object(get_type_id(datum)); 75 | SwigPyClientData *cl_data = (SwigPyClientData*)ty_info->clientdata; 76 | // Call type object to invoke constructor 77 | PyObject* args = PyTuple_Pack(1, py_value); 78 | PyObject* swig_obj = PyObject_CallObject( 79 | (PyObject*)cl_data->pytype, args); 80 | Py_DECREF(args); 81 | if (!swig_obj) 82 | return NULL; 83 | // Convert constructed object to Exiv2::Value 84 | Exiv2::Value* value = 0; 85 | if (!SWIG_IsOK(SWIG_ConvertPtr( 86 | swig_obj, (void**)&value, $descriptor(Exiv2::Value*), 0))) { 87 | PyErr_SetString( 88 | PyExc_RuntimeError, "set_value_from_py: invalid conversion"); 89 | Py_DECREF(swig_obj); 90 | return NULL; 91 | } 92 | // Set value 93 | datum->setValue(value); 94 | Py_DECREF(swig_obj); 95 | return SWIG_Py_Void(); 96 | }; 97 | } 98 | %extend datum_type { 99 | // Extend Metadatum to allow getting value as a specific type. 100 | Exiv2::Value::SMART_PTR getValue(Exiv2::TypeId as_type) { 101 | // deprecated since 2023-12-07 102 | PyErr_WarnEx(PyExc_DeprecationWarning, "Requested type ignored.", 1); 103 | return $self->getValue(); 104 | } 105 | const Exiv2::Value& value(Exiv2::TypeId as_type) { 106 | // deprecated since 2023-12-07 107 | PyErr_WarnEx(PyExc_DeprecationWarning, "Requested type ignored.", 1); 108 | return $self->value(); 109 | } 110 | // Set the value from a Python object. The datum's current or default 111 | // type is used to create an Exiv2::Value object (via Python) from the 112 | // Python object. 113 | %fragment("set_value_from_py"{datum_type}); 114 | PyObject* setValue(PyObject* py_value) { 115 | return set_value_from_py($self, py_value); 116 | } 117 | // Old _print method for compatibility 118 | std::string _print(const Exiv2::ExifData* pMetadata) const { 119 | // deprecated since 2024-01-29 120 | PyErr_WarnEx(PyExc_DeprecationWarning, 121 | "'_print' has been replaced by 'print'", 1); 122 | return $self->print(pMetadata); 123 | } 124 | } 125 | %enddef // EXTEND_METADATUM 126 | 127 | // Extend base type 128 | %feature("python:slot", "tp_str", functype="reprfunc") 129 | Exiv2::Metadatum::__str__; 130 | %extend Exiv2::Metadatum { 131 | std::string __str__() { 132 | return $self->key() + ": " + $self->print(); 133 | } 134 | } 135 | 136 | %ignore Exiv2::Key::~Key; 137 | %ignore Exiv2::Key::operator=; 138 | %ignore Exiv2::Metadatum::~Metadatum; 139 | %ignore Exiv2::Metadatum::operator=; 140 | %ignore Exiv2::cmpMetadataByKey; 141 | %ignore Exiv2::cmpMetadataByTag; 142 | 143 | %include "exiv2/metadatum.hpp" 144 | -------------------------------------------------------------------------------- /src/interface/preview.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") preview 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "Access to preview images. 22 | 23 | For Exif thumbnail images see the :py:class:`ExifThumb` class."; 24 | #endif 25 | 26 | // We don't need Python access to SwigPyIterator 27 | %ignore SwigPyIterator; 28 | 29 | %include "shared/preamble.i" 30 | %include "shared/buffers.i" 31 | %include "shared/exception.i" 32 | %include "shared/exv_options.i" 33 | %include "shared/keep_reference.i" 34 | %include "shared/struct_dict.i" 35 | %include "shared/windows_path.i" 36 | 37 | %include "std_string.i" 38 | %include "std_vector.i" 39 | 40 | %import "image.i"; 41 | 42 | // Catch all C++ exceptions 43 | EXCEPTION() 44 | 45 | %fragment("EXV_USE_CURL"); 46 | %fragment("EXV_USE_SSH"); 47 | %fragment("EXV_ENABLE_FILESYSTEM"); 48 | EXV_ENABLE_FILESYSTEM_FUNCTION(Exiv2::PreviewImage::writeFile) 49 | 50 | // Some calls don't raise exceptions 51 | %noexception Exiv2::PreviewImage::__len__; 52 | %noexception Exiv2::PreviewImage::extension; 53 | %noexception Exiv2::PreviewImage::height; 54 | %noexception Exiv2::PreviewImage::id; 55 | %noexception Exiv2::PreviewImage::mimeType; 56 | %noexception Exiv2::PreviewImage::pData; 57 | %noexception Exiv2::PreviewImage::size; 58 | %noexception Exiv2::PreviewImage::wextension; 59 | %noexception Exiv2::PreviewImage::width; 60 | 61 | // Convert path encoding on Windows 62 | WINDOWS_PATH(const std::string& path) 63 | WINDOWS_PATH_OUT(extension) 64 | 65 | // Convert getPreviewProperties result to a Python tuple 66 | %template() std::vector; 67 | 68 | // Make sure PreviewManager keeps a reference to the image it's using 69 | KEEP_REFERENCE_EX(Exiv2::PreviewManager*, args) 70 | 71 | // Enable len(PreviewImage) 72 | %feature("python:slot", "sq_length", functype="lenfunc") 73 | Exiv2::PreviewImage::__len__; 74 | %extend Exiv2::PreviewImage { 75 | size_t __len__() { 76 | return $self->size(); 77 | } 78 | } 79 | 80 | // Expose Exiv2::PreviewImage contents as a Python buffer 81 | %fragment("get_ptr_size"{Exiv2::PreviewImage}, "header") { 82 | static bool get_ptr_size(Exiv2::PreviewImage* self, bool is_writeable, 83 | Exiv2::byte*& ptr, Py_ssize_t& size) { 84 | ptr = (Exiv2::byte*)self->pData(); 85 | size = self->size(); 86 | return true; 87 | }; 88 | } 89 | EXPOSE_OBJECT_BUFFER(Exiv2::PreviewImage, false, false) 90 | 91 | // Convert pData result to a Python memoryview 92 | RETURN_VIEW(Exiv2::byte* pData, arg1->size(), PyBUF_READ, 93 | Exiv2::PreviewImage::pData) 94 | 95 | // Give Exiv2::PreviewProperties dict-like behaviour 96 | STRUCT_DICT(Exiv2::PreviewProperties) 97 | 98 | %immutable Exiv2::PreviewProperties::mimeType_; 99 | %immutable Exiv2::PreviewProperties::extension_; 100 | %immutable Exiv2::PreviewProperties::wextension_; 101 | %immutable Exiv2::PreviewProperties::size_; 102 | %immutable Exiv2::PreviewProperties::width_; 103 | %immutable Exiv2::PreviewProperties::height_; 104 | %immutable Exiv2::PreviewProperties::id_; 105 | 106 | %ignore Exiv2::PreviewImage::operator=; 107 | %ignore Exiv2::PreviewProperties::PreviewProperties; 108 | 109 | %include "exiv2/preview.hpp" 110 | -------------------------------------------------------------------------------- /src/interface/properties.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") properties 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "XMP key class and data attributes."; 22 | #endif 23 | 24 | %include "shared/preamble.i" 25 | %include "shared/exception.i" 26 | %include "shared/enum.i" 27 | %include "shared/static_list.i" 28 | %include "shared/struct_dict.i" 29 | %include "shared/unique_ptr.i" 30 | 31 | %import "metadatum.i" 32 | 33 | IMPORT_ENUM(TypeId) 34 | 35 | // Catch all C++ exceptions... 36 | EXCEPTION() 37 | 38 | // ...except these 39 | %noexception Exiv2::XmpKey::~XmpKey; 40 | %noexception Exiv2::XmpKey::familyName; 41 | %noexception Exiv2::XmpKey::groupName; 42 | %noexception Exiv2::XmpKey::key; 43 | %noexception Exiv2::XmpKey::tag; 44 | %noexception Exiv2::XmpKey::tagLabel; 45 | %noexception Exiv2::XmpKey::tagName; 46 | %noexception Exiv2::XmpProperties::prefix; 47 | %noexception Exiv2::XmpProperties::propertyDesc; 48 | %noexception Exiv2::XmpProperties::propertyInfo; 49 | %noexception Exiv2::XmpProperties::propertyTitle; 50 | %noexception Exiv2::XmpProperties::propertyType; 51 | 52 | EXTEND_KEY(Exiv2::XmpKey); 53 | 54 | // Make Xmp category more Pythonic 55 | DEFINE_ENUM(XmpCategory, "Category of an XMP property.", 56 | "xmpInternal", Exiv2::xmpInternal, 57 | "xmpExternal", Exiv2::xmpExternal, 58 | "Internal", Exiv2::xmpInternal, 59 | "External", Exiv2::xmpExternal); 60 | 61 | // Get registeredNamespaces to return a Python dict 62 | %typemap(in, numinputs=0) Exiv2::Dictionary &nsDict (Exiv2::Dictionary temp) %{ 63 | $1 = &temp; 64 | %} 65 | %typemap(argout) Exiv2::Dictionary &nsDict { 66 | PyObject* value = NULL; 67 | PyObject* dict = PyDict_New(); 68 | Exiv2::Dictionary::iterator e = $1->end(); 69 | for (Exiv2::Dictionary::iterator i = $1->begin(); i != e; ++i) { 70 | value = PyUnicode_FromString(i->second.c_str()); 71 | PyDict_SetItemString(dict, i->first.c_str(), value); 72 | Py_DECREF(value); 73 | } 74 | $result = SWIG_AppendOutput($result, dict); 75 | } 76 | 77 | // Convert XmpProperties.propertyList() result and XmpNsInfo.xmpPropertyInfo_ 78 | // to a Python list of XmpPropertyInfo objects 79 | // XmpProperties.propertyInfo() returns a single XmpPropertyInfo object 80 | LIST_POINTER(const Exiv2::XmpPropertyInfo* propertyList, 81 | Exiv2::XmpPropertyInfo, name_) 82 | LIST_POINTER(const Exiv2::XmpPropertyInfo* xmpPropertyInfo_, 83 | Exiv2::XmpPropertyInfo, name_) 84 | 85 | // Give Exiv2::XmpPropertyInfo dict-like behaviour 86 | STRUCT_DICT(Exiv2::XmpPropertyInfo) 87 | 88 | // Give Exiv2::XmpNsInfo dict-like behaviour 89 | STRUCT_DICT(Exiv2::XmpNsInfo) 90 | 91 | // Structs are all static data 92 | %ignore Exiv2::XmpPropertyInfo::XmpPropertyInfo; 93 | %ignore Exiv2::XmpPropertyInfo::~XmpPropertyInfo; 94 | %ignore Exiv2::XmpProperties::XmpProperties; 95 | %ignore Exiv2::XmpProperties::~XmpProperties; 96 | %ignore Exiv2::XmpNsInfo::XmpNsInfo; 97 | %ignore Exiv2::XmpNsInfo::~XmpNsInfo; 98 | 99 | // Ignore "internal" stuff 100 | %ignore Exiv2::XmpProperties::rwLock_; 101 | %ignore Exiv2::XmpProperties::mutex_; 102 | %ignore Exiv2::XmpProperties::nsRegistry_; 103 | %ignore Exiv2::XmpPropertyInfo::operator==; 104 | %ignore Exiv2::XmpNsInfo::operator==; 105 | %ignore Exiv2::XmpNsInfo::Prefix; 106 | %ignore Exiv2::XmpNsInfo::Ns; 107 | %ignore NsRegistry; 108 | 109 | // Ignore stuff Python can't use 110 | %ignore Exiv2::XmpProperties::lookupNsRegistry; 111 | %ignore Exiv2::XmpProperties::printProperties; 112 | %ignore Exiv2::XmpProperties::printProperty; 113 | 114 | %immutable; 115 | %include "exiv2/properties.hpp" 116 | %mutable; 117 | -------------------------------------------------------------------------------- /src/interface/shared/buffers.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Macro for input read only byte buffer 20 | %define INPUT_BUFFER_RO(buf_type, len_type) 21 | %typemap(doctype) buf_type ":py:term:`bytes-like object`"; 22 | %typemap(in) (buf_type, len_type) (PyObject* _global_view = NULL) { 23 | _global_view = PyMemoryView_GetContiguous($input, PyBUF_READ, 'A'); 24 | if (!_global_view) { 25 | PyErr_Clear(); 26 | %argument_fail( 27 | SWIG_TypeError, "bytes-like object", $symname, $argnum); 28 | } 29 | Py_buffer* buff = PyMemoryView_GET_BUFFER(_global_view); 30 | $1 = ($1_ltype) buff->buf; 31 | $2 = ($2_ltype) buff->len; 32 | } 33 | %typemap(freearg) (buf_type, len_type) %{ 34 | Py_XDECREF(_global_view); 35 | %} 36 | %typemap(typecheck, precedence=SWIG_TYPECHECK_CHAR_PTR) buf_type %{ 37 | $1 = PyObject_CheckBuffer($input) ? 1 : 0; 38 | %} 39 | %enddef // INPUT_BUFFER_RO 40 | 41 | 42 | // Macro for input read only byte buffer, result keeps reference to input 43 | %define INPUT_BUFFER_RO_EX(buf_type, len_type) 44 | INPUT_BUFFER_RO(buf_type, len_type) 45 | %typemap(argout) (buf_type, len_type) %{ 46 | PyObject_SetAttrString(resultobj, "_refers_to", _global_view); 47 | %} 48 | %enddef // INPUT_BUFFER_RO_EX 49 | 50 | 51 | // Macro for output writeable byte buffer 52 | %define OUTPUT_BUFFER_RW(buf_type, count_type) 53 | %typemap(doctype) buf_type "writeable :py:term:`bytes-like object`"; 54 | %typemap(in) (buf_type) (Py_buffer _global_view) { 55 | _global_view.obj = NULL; 56 | if (PyObject_GetBuffer( 57 | $input, &_global_view, PyBUF_CONTIG | PyBUF_WRITABLE) < 0) { 58 | PyErr_Clear(); 59 | %argument_fail(SWIG_TypeError, "writable bytes-like object", 60 | $symname, $argnum); 61 | } 62 | $1 = ($1_ltype) _global_view.buf; 63 | } 64 | %typemap(check) (buf_type, count_type) { 65 | if ($2 > ($2_ltype) _global_view.len) { 66 | %argument_fail(SWIG_ValueError, "buffer too small", 67 | $symname, $argnum); 68 | } 69 | } 70 | %typemap(freearg) (buf_type) %{ 71 | if (_global_view.obj) { 72 | PyBuffer_Release(&_global_view); 73 | } 74 | %} 75 | %typemap(typecheck, precedence=SWIG_TYPECHECK_CHAR_PTR) buf_type %{ 76 | $1 = PyObject_CheckBuffer($input) ? 1 : 0; 77 | %} 78 | %enddef // OUTPUT_BUFFER_RW 79 | 80 | // Macro to convert byte* return value to memoryview 81 | // WARNING: return value does not keep a reference to the data it points to 82 | %define RETURN_VIEW(signature, size_func, flags, doc_method) 83 | %typemap(doctype) signature "memoryview"; 84 | %typemap(out) (signature) %{ 85 | $result = PyMemoryView_FromMemory((char*)$1, size_func, flags); 86 | %} 87 | #if #doc_method != "" 88 | %feature("docstring") doc_method 89 | "Returns a temporary Python memoryview of the object's data. 90 | 91 | WARNING: do not resize or delete the object while using the view. 92 | 93 | :rtype: memoryview" 94 | #endif 95 | %enddef // RETURN_VIEW 96 | 97 | 98 | // Macros to expose object data with a buffer interface 99 | %define _BF_GETBUFFER(object_type, writeable, get_func) 100 | %fragment("getbuffer"{object_type}, "header", 101 | fragment="get_ptr_size"{object_type}) { 102 | static int getbuffer_%mangle(object_type)( 103 | PyObject* exporter, Py_buffer* view, int flags) { 104 | object_type* self = 0; 105 | Exiv2::byte* ptr = 0; 106 | Py_ssize_t size = 0; 107 | bool is_writeable = writeable && (flags && PyBUF_WRITABLE); 108 | if (!SWIG_IsOK(SWIG_ConvertPtr( 109 | exporter, (void**)&self, $descriptor(object_type*), 0))) 110 | goto fail; 111 | if (!get_ptr_size(self, is_writeable, ptr, size)) 112 | goto fail; 113 | return PyBuffer_FillInfo(view, exporter, ptr, 114 | ptr ? size : 0, is_writeable ? 0 : 1, flags); 115 | fail: 116 | PyErr_SetNone(PyExc_BufferError); 117 | view->obj = NULL; 118 | return -1; 119 | }; 120 | } 121 | %fragment("getbuffer"{object_type}); 122 | %feature("python:bf_getbuffer", functype="getbufferproc") 123 | object_type "get_func"; 124 | %enddef // _BF_GETBUFFER 125 | 126 | %define _BF_RELEASEBUFFER(object_type, release_func) 127 | %fragment("releasebuffer"{object_type}, "header", 128 | fragment="release_ptr"{object_type}) { 129 | static void releasebuffer_%mangle(object_type)( 130 | PyObject* exporter, Py_buffer* view) { 131 | object_type* self = 0; 132 | if (!SWIG_IsOK(SWIG_ConvertPtr( 133 | exporter, (void**)&self, $descriptor(object_type*), 0))) 134 | return; 135 | release_ptr(self); 136 | }; 137 | } 138 | %fragment("releasebuffer"{object_type}); 139 | %feature("python:bf_releasebuffer", functype="releasebufferproc") 140 | object_type "release_func"; 141 | %enddef // _BF_RELEASEBUFFER 142 | 143 | %define EXPOSE_OBJECT_BUFFER(object_type, writeable, with_release) 144 | // Add getbuffer slot to an object type 145 | _BF_GETBUFFER(object_type, writeable, getbuffer_%mangle(object_type)) 146 | #if #with_release == "true" 147 | // Add releasebuffer slot to an object type (not often needed) 148 | _BF_RELEASEBUFFER(object_type, releasebuffer_%mangle(object_type)) 149 | #endif 150 | %enddef // EXPOSE_OBJECT_BUFFER 151 | -------------------------------------------------------------------------------- /src/interface/shared/containers.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Macro to wrap data containers. 20 | %define DATA_CONTAINER(base_class, datum_type, key_type) 21 | // Turn off exception checking for methods that are guaranteed not to throw 22 | %noexception base_class::begin; 23 | %noexception base_class::end; 24 | %noexception base_class::clear; 25 | %noexception base_class::count; 26 | %noexception base_class::empty; 27 | // Add dict-like behaviour 28 | %feature("python:slot", "tp_iter", functype="getiterfunc") 29 | base_class::begin; 30 | %feature("python:slot", "mp_length", functype="lenfunc") 31 | base_class::count; 32 | %feature("python:slot", "mp_subscript", functype="binaryfunc") 33 | base_class::__getitem__; 34 | %feature("python:slot", "mp_ass_subscript", functype="objobjargproc") 35 | base_class::__setitem__; 36 | %feature("python:slot", "sq_contains", functype="objobjproc") 37 | base_class::__contains__; 38 | %extend base_class { 39 | %fragment("get_type_id"{datum_type}); 40 | %fragment("set_value_from_py"{datum_type}); 41 | datum_type& __getitem__(const std::string& key) { 42 | return (*$self)[key]; 43 | } 44 | PyObject* __setitem__(const std::string& key, Exiv2::Value* value) { 45 | datum_type* datum = &(*$self)[key]; 46 | datum->setValue(value); 47 | return SWIG_Py_Void(); 48 | } 49 | PyObject* __setitem__(const std::string& key, const std::string& value) { 50 | datum_type* datum = &(*$self)[key]; 51 | Exiv2::TypeId old_type = get_type_id(datum); 52 | if (datum->setValue(value) != 0) 53 | return PyErr_Format(PyExc_ValueError, 54 | "%s: cannot set type '%s' to value '%s'", 55 | datum->key().c_str(), Exiv2::TypeInfo::typeName(old_type), 56 | value.c_str()); 57 | return SWIG_Py_Void(); 58 | } 59 | PyObject* __setitem__(const std::string& key, PyObject* py_value) { 60 | datum_type* datum = &(*$self)[key]; 61 | return set_value_from_py(datum, py_value); 62 | } 63 | PyObject* __setitem__(const std::string& key) { 64 | base_class::iterator pos = $self->findKey(key_type(key)); 65 | if (pos == $self->end()) { 66 | PyErr_SetString(PyExc_KeyError, key.c_str()); 67 | return NULL; 68 | } 69 | $self->erase(pos); 70 | return SWIG_Py_Void(); 71 | } 72 | bool __contains__(const std::string& key) { 73 | return $self->findKey(key_type(key)) != $self->end(); 74 | } 75 | } 76 | %enddef // DATA_CONTAINER 77 | -------------------------------------------------------------------------------- /src/interface/shared/data_iterator.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | %include "shared/keep_reference.i" 20 | 21 | 22 | // Macros to wrap data iterators 23 | %define DATA_ITERATOR_CLASSES(container_type, datum_type) 24 | %feature("python:slot", "tp_str", functype="reprfunc") 25 | container_type##_iterator_base::__str__; 26 | %feature("python:slot", "tp_iter", functype="getiterfunc") 27 | container_type##_iterator_base::__iter__; 28 | %feature("python:slot", "tp_iternext", functype="iternextfunc") 29 | container_type##_iterator_base::__next__; 30 | %noexception container_type##_iterator_base::__iter__; 31 | %noexception container_type##_iterator_base::operator==; 32 | %noexception container_type##_iterator_base::operator!=; 33 | %ignore container_type##_iterator_base::size; 34 | %ignore container_type##_iterator_base::##container_type##_iterator_base; 35 | %ignore container_type##_iterator_base::operator*; 36 | %ignore container_type##_iterator_base::valid; 37 | %feature("docstring") container_type##_iterator " 38 | Python wrapper for an :class:`" #container_type "` iterator. It has most of 39 | the methods of :class:`" #datum_type "` allowing easy access to the 40 | data it points to." 41 | %feature("docstring") container_type##_iterator_base " 42 | Python wrapper for an :class:`" #container_type "` iterator that points to 43 | the 'end' value and can not be dereferenced." 44 | // Creating a new iterator keeps a reference to the current one 45 | KEEP_REFERENCE(container_type##_iterator*) 46 | KEEP_REFERENCE(container_type##_iterator_base*) 47 | // Detect end of iteration 48 | %exception container_type##_iterator_base::__next__ %{ 49 | $action 50 | if (!result) { 51 | PyErr_SetNone(PyExc_StopIteration); 52 | SWIG_fail; 53 | } 54 | %} 55 | %inline %{ 56 | // Base class implements all methods except dereferencing 57 | class container_type##_iterator_base { 58 | protected: 59 | Exiv2::container_type::iterator ptr; 60 | Exiv2::container_type::iterator end; 61 | Exiv2::container_type::iterator safe_ptr; 62 | public: 63 | container_type##_iterator_base(Exiv2::container_type::iterator ptr, 64 | Exiv2::container_type::iterator end) { 65 | this->ptr = ptr; 66 | this->end = end; 67 | safe_ptr = ptr; 68 | } 69 | container_type##_iterator_base* __iter__() { return this; } 70 | Exiv2::datum_type* __next__() { 71 | if (!valid()) 72 | return NULL; 73 | Exiv2::datum_type* result = &(*safe_ptr); 74 | ptr++; 75 | if (valid()) 76 | safe_ptr = ptr; 77 | return result; 78 | } 79 | Exiv2::container_type::iterator operator*() const { return ptr; } 80 | bool operator==(const container_type##_iterator_base &other) const { 81 | return *other == ptr; 82 | } 83 | bool operator!=(const container_type##_iterator_base &other) const { 84 | return *other != ptr; 85 | } 86 | std::string __str__() { 87 | if (valid()) 88 | return "iterator<" + ptr->key() + ": " + ptr->print() + ">"; 89 | return "iterator"; 90 | } 91 | bool valid() { return ptr != end; } 92 | // Provide size() C++ method for buffer size check 93 | size_t size() { 94 | if (valid()) 95 | return safe_ptr->size(); 96 | return 0; 97 | } 98 | }; 99 | // Derived class can be dereferenced, giving Python access to all datum 100 | // methods. 101 | class container_type##_iterator : public container_type##_iterator_base { 102 | public: 103 | Exiv2::datum_type* operator->() const { return &(*safe_ptr); } 104 | }; 105 | %} 106 | %enddef // DATA_ITERATOR_CLASSES 107 | 108 | // Declare typemaps for data iterators. 109 | %define DATA_ITERATOR_TYPEMAPS(container_type) 110 | %typemap(in) Exiv2::container_type::iterator { 111 | container_type##_iterator_base *argp = NULL; 112 | int res = SWIG_ConvertPtr($input, (void**)&argp, 113 | $descriptor(container_type##_iterator_base*), 0); 114 | if (!SWIG_IsOK(res)) { 115 | %argument_fail(res, 116 | container_type##_iterator_base, $symname, $argnum); 117 | } 118 | if (!argp) { 119 | %argument_nullref(container_type##_iterator_base, $symname, $argnum); 120 | } 121 | $1 = **argp; 122 | } 123 | // XmpData::eraseFamily takes an iterator reference (and invalidates it) 124 | %typemap(in) Exiv2::container_type::iterator& 125 | (Exiv2::container_type::iterator it) { 126 | container_type##_iterator_base* argp = NULL; 127 | int res = SWIG_ConvertPtr($input, (void**)&argp, 128 | $descriptor(container_type##_iterator_base*), 0); 129 | if (!SWIG_IsOK(res)) { 130 | %argument_fail(res, 131 | container_type##_iterator_base, $symname, $argnum); 132 | } 133 | if (!argp) { 134 | %argument_nullref(container_type##_iterator_base, $symname, $argnum); 135 | } 136 | it = **argp; 137 | $1 = ⁢ 138 | } 139 | // Return types depend on validity of iterator 140 | %typemap(out) container_type##_iterator_base* { 141 | $result = SWIG_NewPointerObj((void*)$1, 142 | $1->valid() ? $descriptor(container_type##_iterator*) : 143 | $descriptor(container_type##_iterator_base*), 0); 144 | } 145 | // Assumes arg1 is the base class parent 146 | %typemap(out) Exiv2::container_type::iterator { 147 | container_type##_iterator_base* tmp = new container_type##_iterator_base($1, arg1->end()); 148 | $result = SWIG_NewPointerObj((void*)tmp, 149 | tmp->valid() ? $descriptor(container_type##_iterator*) : 150 | $descriptor(container_type##_iterator_base*), 151 | SWIG_POINTER_OWN); 152 | } 153 | // Keep a reference to the data being iterated 154 | KEEP_REFERENCE(Exiv2::container_type::iterator) 155 | %enddef // DATA_ITERATOR_TYPEMAPS 156 | -------------------------------------------------------------------------------- /src/interface/shared/exception.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2024 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %include "shared/enum.i" 19 | %include "shared/windows_cp.i" 20 | 21 | %include "exception.i" 22 | 23 | IMPORT_ENUM(ErrorCode) 24 | 25 | // Import PyExc_Exiv2Error exception 26 | %fragment("_import_exception_decl", "header") { 27 | static PyObject* PyExc_Exiv2Error = NULL; 28 | } 29 | %fragment("_import_exception", "init", fragment="_import_exception_decl", 30 | fragment="import_exiv2") { 31 | { 32 | PyExc_Exiv2Error = PyObject_GetAttrString(exiv2_module, "Exiv2Error"); 33 | if (!PyExc_Exiv2Error) 34 | return NULL; 35 | } 36 | } 37 | 38 | // Function that re-raises an exception to handle different types 39 | %fragment("_set_python_exception", "header", 40 | fragment="_import_exception", 41 | fragment="py_from_enum"{Exiv2::ErrorCode}, 42 | fragment="utf8_to_wcp") { 43 | static void _set_python_exception() { 44 | try { 45 | throw; 46 | } 47 | #if EXIV2_VERSION_HEX < 0x001c0000 48 | catch(Exiv2::AnyError const& e) { 49 | std::string msg = e.what(); 50 | if (wcp_to_utf8(&msg)) 51 | msg = e.what(); 52 | PyObject* args = Py_BuildValue( 53 | "Ns", py_from_enum((Exiv2::ErrorCode)e.code()), msg.c_str()); 54 | PyErr_SetObject(PyExc_Exiv2Error, args); 55 | Py_DECREF(args); 56 | } 57 | #else 58 | catch(Exiv2::Error const& e) { 59 | std::string msg = e.what(); 60 | if (wcp_to_utf8(&msg)) 61 | msg = e.what(); 62 | PyObject* args = Py_BuildValue( 63 | "Ns", py_from_enum((Exiv2::ErrorCode)e.code()), msg.c_str()); 64 | PyErr_SetObject(PyExc_Exiv2Error, args); 65 | Py_DECREF(args); 66 | } 67 | #endif 68 | SWIG_CATCH_STDEXCEPT 69 | fail: 70 | return; 71 | }; 72 | } 73 | 74 | // Macro to define %exception directives 75 | %define EXCEPTION(method) 76 | %fragment("_set_python_exception"); 77 | %exception method { 78 | try { 79 | $action 80 | } 81 | catch(std::exception const& e) { 82 | _set_python_exception(); 83 | SWIG_fail; 84 | } 85 | } 86 | %enddef // EXCEPTION 87 | -------------------------------------------------------------------------------- /src/interface/shared/exv_options.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2024 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This file is part of python-exiv2. python-exiv2 is free software: you can 6 | // redistribute it and/or modify it under the terms of the GNU General Public 7 | // License as published by the Free Software Foundation, either version 3 of 8 | // the License, or (at your option) any later version. 9 | // 10 | // python-exiv2 is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with python-exiv2. If not, see . 17 | 18 | 19 | // Fragment to define Exiv2::CurlIo if EXV_USE_CURL is OFF 20 | %fragment("EXV_USE_CURL", "header") %{ 21 | #ifndef EXV_USE_CURL 22 | namespace Exiv2 { 23 | class CurlIo : public RemoteIo {}; 24 | } 25 | #endif // EXV_USE_CURL 26 | %} 27 | 28 | // Fragment to define Exiv2::SshIo if EXV_USE_SSH is OFF 29 | %fragment("EXV_USE_SSH", "header") %{ 30 | #ifndef EXV_USE_SSH 31 | namespace Exiv2 { 32 | class SshIo : public RemoteIo {}; 33 | } 34 | #endif // EXV_USE_SSH 35 | %} 36 | 37 | // Fragment to set EXV_ENABLE_FILESYSTEM on old libexiv2 versions 38 | %fragment("set_EXV_ENABLE_FILESYSTEM", "header") %{ 39 | #if !EXIV2_TEST_VERSION(0, 28, 3) 40 | #define EXV_ENABLE_FILESYSTEM 41 | #endif 42 | // Copy EXV_ENABLE_FILESYSTEM for use in macro 43 | #ifdef EXV_ENABLE_FILESYSTEM 44 | #define _EXV_ENABLE_FILESYSTEM 45 | #endif 46 | %} 47 | 48 | // Fragment to define FileIo and XPathIo if EXV_ENABLE_FILESYSTEM is OFF 49 | %fragment("EXV_ENABLE_FILESYSTEM", "header", 50 | fragment="set_EXV_ENABLE_FILESYSTEM") %{ 51 | #ifndef EXV_ENABLE_FILESYSTEM 52 | namespace Exiv2 { 53 | class FileIo : public BasicIo {}; 54 | class XPathIo : public MemIo {}; 55 | } 56 | #endif // EXV_ENABLE_FILESYSTEM 57 | %} 58 | 59 | // Macro to not call a function if EXV_ENABLE_FILESYSTEM is OFF 60 | %define EXV_ENABLE_FILESYSTEM_FUNCTION(signature) 61 | %fragment("_set_python_exception"); 62 | %fragment("set_EXV_ENABLE_FILESYSTEM"); 63 | %exception signature { 64 | try { 65 | %#ifdef _EXV_ENABLE_FILESYSTEM 66 | $action 67 | %#else 68 | throw Exiv2::Error(Exiv2::ErrorCode::kerFunctionNotSupported); 69 | %#endif 70 | } 71 | catch(std::exception const& e) { 72 | _set_python_exception(); 73 | SWIG_fail; 74 | } 75 | } 76 | %enddef // EXV_ENABLE_FILESYSTEM_FUNCTION 77 | 78 | // Macro to not call a function if libexiv2 version is <= 0.27.3 79 | %define EXV_ENABLE_EASYACCESS_FUNCTION(signature) 80 | %fragment("_set_python_exception"); 81 | %exception signature { 82 | try { 83 | %#if EXIV2_TEST_VERSION(0, 27, 4) 84 | $action 85 | %#else 86 | throw Exiv2::Error(Exiv2::kerFunctionNotSupported); 87 | %#endif 88 | } 89 | catch(std::exception const& e) { 90 | _set_python_exception(); 91 | SWIG_fail; 92 | } 93 | } 94 | %enddef // EXV_ENABLE_EASYACCESS_FUNCTION 95 | -------------------------------------------------------------------------------- /src/interface/shared/keep_reference.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Macro to keep a reference to any object when returning a particular type. 20 | %define KEEP_REFERENCE_EX(return_type, target) 21 | %typemap(ret) return_type %{ 22 | if ($result != Py_None) 23 | if (PyObject_SetAttrString($result, "_refers_to", target)) { 24 | SWIG_fail; 25 | } 26 | %} 27 | %enddef // KEEP_REFERENCE_EX 28 | 29 | // Macro to keep a reference to "self" when returning a particular type. 30 | %define KEEP_REFERENCE(return_type) 31 | KEEP_REFERENCE_EX(return_type, self) 32 | %enddef // KEEP_REFERENCE 33 | -------------------------------------------------------------------------------- /src/interface/shared/preamble.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %{ 19 | #include "exiv2/exiv2.hpp" 20 | %} 21 | 22 | // EXIV2API prepends every function declaration 23 | #define EXIV2API 24 | // Some have this instead 25 | #define EXIV2LIB_DEPRECATED_EXPORT 26 | // Older versions of libexiv2 define these as well 27 | #define EXV_DLLLOCAL 28 | #define EXV_DLLPUBLIC 29 | 30 | %typemap(doctype) bool "bool" 31 | %typemap(doctype) Exiv2::byte "int" 32 | %typemap(doctype) std::string "str" 33 | -------------------------------------------------------------------------------- /src/interface/shared/static_list.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Macro to convert pointer to static list to a Python list of objects 20 | %define LIST_POINTER(pattern, item_type, valid_test) 21 | %fragment("pointer_to_list"{item_type}, "header") { 22 | static PyObject* pointer_to_list(item_type* ptr) { 23 | PyObject* list = PyList_New(0); 24 | if (!ptr) 25 | return list; 26 | PyObject* py_tmp = NULL; 27 | while (ptr->valid_test) { 28 | py_tmp = SWIG_Python_NewPointerObj( 29 | NULL, ptr, $descriptor(item_type*), 0); 30 | PyList_Append(list, py_tmp); 31 | Py_DECREF(py_tmp); 32 | ++ptr; 33 | } 34 | return list; 35 | }; 36 | } 37 | %typemap(out, fragment="pointer_to_list"{item_type}) pattern { 38 | PyObject* list = pointer_to_list($1); 39 | if (!list) 40 | SWIG_fail; 41 | $result = SWIG_AppendOutput($result, list); 42 | } 43 | %enddef // LIST_POINTER 44 | -------------------------------------------------------------------------------- /src/interface/shared/struct_dict.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2024 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Add dict-like behaviour to an Exiv2 struct, e.g. PreviewProperties 20 | 21 | // Helper functions 22 | %fragment("getset_functions", "header") { 23 | static PyObject* list_getset( 24 | PyObject* obj, PyObject* (*conv)(PyObject*, PyGetSetDef*)) { 25 | PyGetSetDef* getset = obj->ob_type->tp_getset; 26 | PyObject* result = PyList_New(0); 27 | PyObject* item = NULL; 28 | while (getset->name) { 29 | if (getset->name[0] != '_') { 30 | item = (*conv)(obj, getset); 31 | PyList_Append(result, item); 32 | Py_DECREF(item); 33 | } 34 | getset++; 35 | } 36 | return result; 37 | }; 38 | static PyGetSetDef* find_getset(PyObject* obj, const char* name) { 39 | size_t len = strlen(name); 40 | PyGetSetDef* getset = obj->ob_type->tp_getset; 41 | while (getset->name) { 42 | size_t cmp_len = strlen(getset->name); 43 | if (getset->name[cmp_len-1] == '_') 44 | cmp_len--; 45 | if ((cmp_len == len) && (strncmp(getset->name, name, len) == 0)) 46 | return getset; 47 | getset++; 48 | } 49 | PyErr_Format( 50 | PyExc_KeyError, "'%s' not in '%s'", name, obj->ob_type->tp_name); 51 | return NULL; 52 | }; 53 | static PyObject* getset_to_item(PyObject* obj, PyGetSetDef* getset) { 54 | size_t len = strlen(getset->name); 55 | if (getset->name[len-1] == '_') 56 | len--; 57 | return Py_BuildValue("(s#N)", getset->name, len, 58 | getset->get(obj, getset->closure)); 59 | }; 60 | static PyObject* getset_to_key(PyObject* obj, PyGetSetDef* getset) { 61 | size_t len = strlen(getset->name); 62 | if (getset->name[len-1] == '_') 63 | len--; 64 | return Py_BuildValue("s#", getset->name, len); 65 | }; 66 | static PyObject* getset_to_value(PyObject* obj, PyGetSetDef* getset) { 67 | return Py_BuildValue("N", getset->get(obj, getset->closure)); 68 | }; 69 | } 70 | 71 | // Macro definition 72 | %define STRUCT_DICT(struct_type) 73 | // Type slots 74 | %feature("python:slot", "tp_iter", functype="getiterfunc") 75 | struct_type::__iter__; 76 | %feature("python:slot", "mp_subscript", functype="binaryfunc") 77 | struct_type::__getitem__; 78 | %feature("python:slot", "mp_ass_subscript", functype="objobjargproc") 79 | struct_type::__setitem__; 80 | // Typemaps for slot functions 81 | %typemap(in, numinputs=0) PyObject* py_self {$1 = self;} 82 | %typemap(default) PyObject* value {$1 = NULL;} 83 | // Document functions 84 | %feature("docstring") struct_type::items "Get structure members. 85 | :rtype: list of (str, value) tuple 86 | :return: structure member (name, value) pairs (with any trailing 87 | underscores removed from names)." 88 | %feature("docstring") struct_type::keys "Get structure member names. 89 | :rtype: list of str 90 | :return: structure member names (with any trailing underscores 91 | removed)." 92 | %feature("docstring") struct_type::values "Get structure member values. 93 | :rtype: list of value 94 | :return: structure member values." 95 | // Add functions 96 | %extend struct_type { 97 | %fragment("getset_functions"); 98 | PyObject* items(PyObject* py_self) { 99 | return list_getset(py_self, getset_to_item); 100 | } 101 | PyObject* keys(PyObject* py_self) { 102 | return list_getset(py_self, getset_to_key); 103 | } 104 | PyObject* values(PyObject* py_self) { 105 | return list_getset(py_self, getset_to_value); 106 | } 107 | PyObject* __iter__(PyObject* py_self) { 108 | PyObject* seq = 109 | %mangle(struct_type::keys)($self, py_self); 110 | PyObject* result = PySeqIter_New(seq); 111 | Py_DECREF(seq); 112 | return result; 113 | } 114 | PyObject* __getitem__(PyObject* py_self, const std::string& key) { 115 | PyGetSetDef* getset = find_getset(py_self, key.c_str()); 116 | if (!getset) 117 | return NULL; 118 | return getset->get(py_self, getset->closure); 119 | } 120 | PyObject* __setitem__(PyObject* py_self, const std::string& key, 121 | PyObject* value) { 122 | PyGetSetDef* getset = find_getset(py_self, key.c_str()); 123 | if (!getset) 124 | return NULL; 125 | if (!value) 126 | return PyErr_Format(PyExc_TypeError, 127 | "%s['%s'] can not be deleted", py_self->ob_type->tp_name, 128 | key.c_str()); 129 | if (!getset->set) 130 | return PyErr_Format(PyExc_TypeError, "%s['%s'] is read-only", 131 | py_self->ob_type->tp_name, key.c_str()); 132 | if (getset->set(py_self, value, getset->closure) != 0) 133 | return NULL; 134 | return SWIG_Py_Void(); 135 | } 136 | } 137 | %enddef // STRUCT_DICT 138 | -------------------------------------------------------------------------------- /src/interface/shared/unique_ptr.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Stuff to handle auto_ptr or unique_ptr 20 | #if EXIV2_VERSION_HEX < 0x001c0000 21 | #define SMART_PTR AutoPtr 22 | %ignore AutoPtr; 23 | %define UNIQUE_PTR(pointed_type) 24 | %include "std_auto_ptr.i" 25 | %typemap(doctype) pointed_type##::AutoPtr #pointed_type 26 | %auto_ptr(pointed_type) 27 | %enddef // UNIQUE_PTR 28 | #else 29 | #define SMART_PTR UniquePtr 30 | %ignore UniquePtr; 31 | #if SWIG_VERSION >= 0x040100 32 | %define UNIQUE_PTR(pointed_type) 33 | %include "std_unique_ptr.i" 34 | %typemap(doctype) pointed_type##::UniquePtr #pointed_type 35 | %unique_ptr(pointed_type) 36 | %enddef // UNIQUE_PTR 37 | #else 38 | template 39 | struct std::unique_ptr {}; 40 | %define UNIQUE_PTR(pointed_type) 41 | %typemap(out) std::unique_ptr %{ 42 | $result = SWIG_NewPointerObj( 43 | $1.release(), $descriptor(pointed_type *), SWIG_POINTER_OWN); 44 | %} 45 | %template() std::unique_ptr; 46 | %enddef // UNIQUE_PTR 47 | #endif 48 | #endif 49 | -------------------------------------------------------------------------------- /src/interface/shared/windows_cp.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2024 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | // Function to convert utf-8 string to/from current Windows code page 20 | %fragment("utf8_to_wcp", "header") %{ 21 | #ifdef _WIN32 22 | #include 23 | #endif 24 | 25 | #ifdef _WIN32 26 | static int _transcode(std::string *str, UINT cp_in, UINT cp_out) { 27 | if (cp_out == cp_in) 28 | return 0; 29 | int size = MultiByteToWideChar(cp_in, 0, &(*str)[0], (int)str->size(), 30 | NULL, 0); 31 | if (!size) 32 | return GetLastError(); 33 | std::wstring wide_str; 34 | wide_str.resize(size); 35 | if (!MultiByteToWideChar(cp_in, 0, &(*str)[0], (int)str->size(), 36 | &wide_str[0], size)) 37 | return GetLastError(); 38 | size = WideCharToMultiByte(cp_out, 0, &wide_str[0], (int)wide_str.size(), 39 | NULL, 0, NULL, NULL); 40 | if (!size) 41 | return GetLastError(); 42 | str->resize(size); 43 | if (!WideCharToMultiByte(cp_out, 0, &wide_str[0], (int)wide_str.size(), 44 | &(*str)[0], size, NULL, NULL)) 45 | return GetLastError(); 46 | return 0; 47 | }; 48 | #endif 49 | 50 | static int utf8_to_wcp(std::string *str) { 51 | #ifdef _WIN32 52 | return _transcode(str, CP_UTF8, GetACP()); 53 | #else 54 | return 0; 55 | #endif 56 | }; 57 | 58 | static int wcp_to_utf8(std::string *str) { 59 | #ifdef _WIN32 60 | return _transcode(str, GetACP(), CP_UTF8); 61 | #else 62 | return 0; 63 | #endif 64 | }; 65 | 66 | %} 67 | -------------------------------------------------------------------------------- /src/interface/shared/windows_path.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | 19 | %include "shared/windows_cp.i" 20 | 21 | // Macro to convert Windows path inputs from utf-8 to current code page 22 | %define WINDOWS_PATH(signature) 23 | %typemap(check, fragment="utf8_to_wcp") signature { 24 | %#ifdef _WIN32 25 | int error = utf8_to_wcp($1); 26 | if (error) { 27 | PyErr_SetFromWindowsErr(error); 28 | SWIG_fail; 29 | } 30 | %#endif 31 | } 32 | %enddef // WINDOWS_PATH 33 | 34 | // Macro to convert Windows path outputs from current code page to utf-8 35 | %define WINDOWS_PATH_OUT(function) 36 | %typemap(out, fragment="utf8_to_wcp") std::string function { 37 | %#ifdef _WIN32 38 | int error = wcp_to_utf8(&$1); 39 | if (error) { 40 | PyErr_SetFromWindowsErr(error); 41 | SWIG_fail; 42 | } 43 | %#endif 44 | $result = SWIG_FromCharPtrAndSize($1.data(), $1.size()); 45 | } 46 | %typemap(out, fragment="utf8_to_wcp") const std::string& function { 47 | std::string copy = *$1; 48 | %#ifdef _WIN32 49 | int error = wcp_to_utf8(©); 50 | if (error) { 51 | PyErr_SetFromWindowsErr(error); 52 | SWIG_fail; 53 | } 54 | %#endif 55 | $result = SWIG_FromCharPtrAndSize(copy.data(), copy.size()); 56 | } 57 | %enddef // WINDOWS_PATH_OUT 58 | -------------------------------------------------------------------------------- /src/interface/tags.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") tags 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "Exif key class and data attributes."; 22 | #endif 23 | 24 | %include "shared/preamble.i" 25 | %include "shared/enum.i" 26 | %include "shared/exception.i" 27 | %include "shared/static_list.i" 28 | %include "shared/struct_dict.i" 29 | %include "shared/unique_ptr.i" 30 | 31 | %import "metadatum.i"; 32 | 33 | IMPORT_ENUM(TypeId) 34 | 35 | // Catch some C++ exceptions 36 | %exception; 37 | EXCEPTION(Exiv2::ExifKey::ExifKey) 38 | EXCEPTION(Exiv2::ExifKey::clone) 39 | 40 | EXTEND_KEY(Exiv2::ExifKey); 41 | 42 | // Add Exif specific enums 43 | #if EXIV2_VERSION_HEX >= 0x001c0000 44 | DEFINE_ENUM(IfdId, "Type to specify the IFD to which a metadata belongs.\n" 45 | "\nMaker note IFDs have been omitted from this enum.", 46 | "ifdIdNotSet", Exiv2::IfdId::ifdIdNotSet, 47 | "ifd0Id", Exiv2::IfdId::ifd0Id, 48 | "ifd1Id", Exiv2::IfdId::ifd1Id, 49 | "ifd2Id", Exiv2::IfdId::ifd2Id, 50 | "ifd3Id", Exiv2::IfdId::ifd3Id, 51 | "exifId", Exiv2::IfdId::exifId, 52 | "gpsId", Exiv2::IfdId::gpsId, 53 | "iopId", Exiv2::IfdId::iopId, 54 | "mpfId", Exiv2::IfdId::mpfId, 55 | "subImage1Id", Exiv2::IfdId::subImage1Id, 56 | "subImage2Id", Exiv2::IfdId::subImage2Id, 57 | "subImage3Id", Exiv2::IfdId::subImage3Id, 58 | "subImage4Id", Exiv2::IfdId::subImage4Id, 59 | "subImage5Id", Exiv2::IfdId::subImage5Id, 60 | "subImage6Id", Exiv2::IfdId::subImage6Id, 61 | "subImage7Id", Exiv2::IfdId::subImage7Id, 62 | "subImage8Id", Exiv2::IfdId::subImage8Id, 63 | "subImage9Id", Exiv2::IfdId::subImage9Id, 64 | "subThumb1Id", Exiv2::IfdId::subThumb1Id, 65 | "lastId", Exiv2::IfdId::lastId, 66 | "ignoreId", Exiv2::IfdId::ignoreId); 67 | 68 | DEFINE_ENUM(SectionId, "Section identifiers to logically group tags.\n" 69 | "\nA section consists of nothing more than a name, based on the" 70 | "\nExif standard.", 71 | "sectionIfNotSet", Exiv2::SectionId::sectionIdNotSet, 72 | "imgStruct", Exiv2::SectionId::imgStruct, 73 | "recOffset", Exiv2::SectionId::recOffset, 74 | "imgCharacter", Exiv2::SectionId::imgCharacter, 75 | "otherTags", Exiv2::SectionId::otherTags, 76 | "exifFormat", Exiv2::SectionId::exifFormat, 77 | "exifVersion", Exiv2::SectionId::exifVersion, 78 | "imgConfig", Exiv2::SectionId::imgConfig, 79 | "userInfo", Exiv2::SectionId::userInfo, 80 | "relatedFile", Exiv2::SectionId::relatedFile, 81 | "dateTime", Exiv2::SectionId::dateTime, 82 | "captureCond", Exiv2::SectionId::captureCond, 83 | "gpsTags", Exiv2::SectionId::gpsTags, 84 | "iopTags", Exiv2::SectionId::iopTags, 85 | "mpfTags", Exiv2::SectionId::mpfTags, 86 | "makerTags", Exiv2::SectionId::makerTags, 87 | "dngTags", Exiv2::SectionId::dngTags, 88 | "panaRaw", Exiv2::SectionId::panaRaw, 89 | "tiffEp", Exiv2::SectionId::tiffEp, 90 | "tiffPm6", Exiv2::SectionId::tiffPm6, 91 | "adobeOpi", Exiv2::SectionId::adobeOpi, 92 | "lastSectionId", Exiv2::SectionId::lastSectionId); 93 | #endif // EXIV2_VERSION_HEX 94 | 95 | // Convert ExifTags::groupList() result to a Python list of GroupInfo objects 96 | LIST_POINTER(const Exiv2::GroupInfo*, Exiv2::GroupInfo, tagList_) 97 | // Convert ExifTags::tagList() result to a Python list of TagInfo objects 98 | LIST_POINTER(const Exiv2::TagInfo*, Exiv2::TagInfo, tag_ != 0xFFFF) 99 | 100 | // Give Exiv2::GroupInfo dict-like behaviour 101 | STRUCT_DICT(Exiv2::GroupInfo) 102 | 103 | // Give Exiv2::TagInfo dict-like behaviour 104 | STRUCT_DICT(Exiv2::TagInfo) 105 | 106 | // Wrapper class for TagListFct function pointer 107 | #ifndef SWIGIMPORTED 108 | %ignore _TagListFct::_TagListFct; 109 | %feature("python:slot", "tp_call", functype="ternarycallfunc") 110 | _TagListFct::__call__; 111 | %noexception _TagListFct::~_TagListFct; 112 | %noexception _TagListFct::__call__; 113 | %inline %{ 114 | class _TagListFct { 115 | private: 116 | Exiv2::TagListFct func; 117 | public: 118 | _TagListFct(Exiv2::TagListFct func) : func(func) {} 119 | const Exiv2::TagInfo* __call__() { 120 | return (*func)(); 121 | } 122 | }; 123 | %} 124 | %fragment("new_TagListFct", "header") { 125 | static PyObject* new_TagListFct(Exiv2::TagListFct func) { 126 | return SWIG_Python_NewPointerObj(NULL, new _TagListFct(func), 127 | $descriptor(_TagListFct*), SWIG_POINTER_OWN); 128 | } 129 | } 130 | #endif // SWIGIMPORTED 131 | 132 | // Wrap TagListFct return values 133 | %typemap(out, fragment="new_TagListFct") Exiv2::TagListFct { 134 | $result = new_TagListFct($1); 135 | } 136 | 137 | // Structs are all static data 138 | %ignore Exiv2::GroupInfo::GroupInfo; 139 | %ignore Exiv2::GroupInfo::~GroupInfo; 140 | %ignore Exiv2::TagInfo::TagInfo; 141 | %ignore Exiv2::TagInfo::~TagInfo; 142 | %ignore Exiv2::ExifTags::~ExifTags; 143 | 144 | // Ignore stuff that Python can't use or doesn't need 145 | %ignore Exiv2::GroupInfo::operator==; 146 | %ignore Exiv2::GroupInfo::GroupName; 147 | %ignore Exiv2::ExifTags::taglist; 148 | %ignore Exiv2::TagInfo::printFct_; 149 | 150 | // Ignore unneeded key constructor 151 | %ignore Exiv2::ExifKey::ExifKey(const TagInfo&); 152 | 153 | // ExifKey::ifdId is documented as internal use only 154 | %ignore Exiv2::ExifKey::ifdId; 155 | 156 | %immutable; 157 | %include "exiv2/tags.hpp" 158 | %mutable; 159 | -------------------------------------------------------------------------------- /src/interface/version.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") version 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "Exiv2 library version information."; 22 | #endif 23 | 24 | %include "shared/preamble.i" 25 | %include "shared/exception.i" 26 | %include "shared/exv_options.i" 27 | 28 | %include "stdint.i" 29 | %include "std_string.i" 30 | 31 | // Catch all C++ exceptions 32 | EXCEPTION() 33 | 34 | // Import __version__ and __version_tuple__ from exiv2 module 35 | %fragment("import_exiv2"); 36 | %constant PyObject* __version__ = PyObject_GetAttrString( 37 | exiv2_module, "__version__"); 38 | %constant PyObject* __version_tuple__ = PyObject_GetAttrString( 39 | exiv2_module, "__version_tuple__"); 40 | 41 | // Function to report build options used 42 | %feature("docstring") versionInfo "Return a dict of libexiv2 build options." 43 | %fragment("set_EXV_ENABLE_FILESYSTEM"); 44 | %inline %{ 45 | static PyObject* versionInfo() { 46 | bool nls = false; 47 | bool bmff = false; 48 | bool video = false; 49 | bool unicode = false; 50 | bool webready = false; 51 | bool curl = false; 52 | bool filesystem = false; 53 | #ifdef EXV_ENABLE_NLS 54 | nls = true; 55 | #endif 56 | #ifdef EXV_ENABLE_BMFF 57 | bmff = true; 58 | #endif 59 | #ifdef EXV_ENABLE_VIDEO 60 | video = true; 61 | #endif 62 | #ifdef EXV_ENABLE_WEBREADY 63 | webready = true; 64 | #endif 65 | #ifdef EXV_USE_CURL 66 | curl = true; 67 | #endif 68 | #ifdef EXV_ENABLE_FILESYSTEM 69 | filesystem = true; 70 | #endif 71 | return Py_BuildValue("{ss,sN,sN,sN,sN,sN,sN,sN}", 72 | "version", Exiv2::version(), 73 | "EXV_ENABLE_NLS", PyBool_FromLong(nls), 74 | "EXV_ENABLE_BMFF", PyBool_FromLong(bmff), 75 | "EXV_ENABLE_VIDEO", PyBool_FromLong(video), 76 | "EXV_UNICODE_PATH", PyBool_FromLong(unicode), 77 | "EXV_ENABLE_WEBREADY", PyBool_FromLong(webready), 78 | "EXV_USE_CURL", PyBool_FromLong(curl), 79 | "EXV_ENABLE_FILESYSTEM", PyBool_FromLong(filesystem)); 80 | }; 81 | %} 82 | 83 | %ignore exv_grep_keys_t; 84 | %ignore Exiv2_grep_key_t; 85 | %ignore Exiv2::dumpLibraryInfo; 86 | %ignore CPLUSPLUS11; 87 | %ignore EXIV2_VERSION; 88 | 89 | %include "exiv2/version.hpp" 90 | -------------------------------------------------------------------------------- /src/interface/xmp.i: -------------------------------------------------------------------------------- 1 | // python-exiv2 - Python interface to libexiv2 2 | // http://github.com/jim-easterbrook/python-exiv2 3 | // Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | %module(package="exiv2") xmp 19 | 20 | #ifndef SWIGIMPORTED 21 | %constant char* __doc__ = "XMP metadatum, container and iterators."; 22 | #endif 23 | 24 | #pragma SWIG nowarn=508 // Declaration of '__str__' shadows declaration accessible via operator->() 25 | 26 | %include "shared/preamble.i" 27 | %include "shared/containers.i" 28 | %include "shared/data_iterator.i" 29 | %include "shared/enum.i" 30 | %include "shared/exception.i" 31 | 32 | %include "stdint.i" 33 | %include "std_string.i" 34 | 35 | %import "properties.i" 36 | 37 | IMPORT_ENUM(ByteOrder) 38 | IMPORT_ENUM(TypeId) 39 | 40 | // Catch all C++ exceptions 41 | EXCEPTION() 42 | 43 | EXTEND_METADATUM(Exiv2::Xmpdatum) 44 | 45 | DATA_ITERATOR_TYPEMAPS(XmpData) 46 | #ifndef SWIGIMPORTED 47 | DATA_ITERATOR_CLASSES(XmpData, Xmpdatum) 48 | #endif 49 | 50 | // Get the current (or default if not set) type id of a datum 51 | %fragment("get_type_id"{Exiv2::Xmpdatum}, "header") { 52 | static Exiv2::TypeId get_type_id(Exiv2::Xmpdatum* datum) { 53 | Exiv2::TypeId type_id = datum->typeId(); 54 | if (type_id != Exiv2::invalidTypeId) 55 | return type_id; 56 | return Exiv2::XmpProperties::propertyType(Exiv2::XmpKey(datum->key())); 57 | }; 58 | } 59 | 60 | DATA_CONTAINER(Exiv2::XmpData, Exiv2::Xmpdatum, Exiv2::XmpKey) 61 | 62 | // Ignore const overloads of some methods 63 | %ignore Exiv2::XmpData::operator[]; 64 | %ignore Exiv2::XmpData::begin() const; 65 | %ignore Exiv2::XmpData::end() const; 66 | %ignore Exiv2::XmpData::findKey(XmpKey const &) const; 67 | %ignore Exiv2::XmpParser::decode; 68 | %ignore Exiv2::XmpParser::encode; 69 | 70 | %include "exiv2/xmp_exiv2.hpp" 71 | -------------------------------------------------------------------------------- /src/swig-0_27_7/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | 5 | if sys.platform == 'win32': 6 | _dir = os.path.join(os.path.dirname(__file__), 'lib') 7 | if os.path.isdir(_dir): 8 | if hasattr(os, 'add_dll_directory'): 9 | os.add_dll_directory(_dir) 10 | os.environ['PATH'] = _dir + ';' + os.environ['PATH'] 11 | 12 | class Exiv2Error(Exception): 13 | """Python exception raised by exiv2 library errors. 14 | 15 | :ivar ErrorCode code: The Exiv2 error code that caused the exception. 16 | :ivar str message: The message associated with the exception. 17 | """ 18 | def __init__(self, code, message): 19 | self.code= code 20 | self.message = message 21 | 22 | #: python-exiv2 version as a string 23 | __version__ = "0.17.3" 24 | #: python-exiv2 version as a tuple of ints 25 | __version_tuple__ = tuple((0, 17, 3)) 26 | 27 | __all__ = ["Exiv2Error"] 28 | from exiv2.basicio import * 29 | __all__ += exiv2._basicio.__all__ 30 | from exiv2.datasets import * 31 | __all__ += exiv2._datasets.__all__ 32 | from exiv2.easyaccess import * 33 | __all__ += exiv2._easyaccess.__all__ 34 | from exiv2.error import * 35 | __all__ += exiv2._error.__all__ 36 | from exiv2.exif import * 37 | __all__ += exiv2._exif.__all__ 38 | from exiv2.image import * 39 | __all__ += exiv2._image.__all__ 40 | from exiv2.iptc import * 41 | __all__ += exiv2._iptc.__all__ 42 | from exiv2.metadatum import * 43 | __all__ += exiv2._metadatum.__all__ 44 | from exiv2.preview import * 45 | __all__ += exiv2._preview.__all__ 46 | from exiv2.properties import * 47 | __all__ += exiv2._properties.__all__ 48 | from exiv2.tags import * 49 | __all__ += exiv2._tags.__all__ 50 | from exiv2.types import * 51 | __all__ += exiv2._types.__all__ 52 | from exiv2.value import * 53 | __all__ += exiv2._value.__all__ 54 | from exiv2.version import * 55 | __all__ += exiv2._version.__all__ 56 | from exiv2.xmp import * 57 | __all__ += exiv2._xmp.__all__ 58 | 59 | __all__ = [x for x in __all__ if x[0] != '_'] 60 | __all__.sort() 61 | -------------------------------------------------------------------------------- /src/swig-0_27_7/__main__.py: -------------------------------------------------------------------------------- 1 | # python-exiv2 - Python interface to exiv2 2 | # http://github.com/jim-easterbrook/python-exiv2 3 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import argparse 19 | import os 20 | import pprint 21 | import sys 22 | 23 | import exiv2 24 | 25 | def main(): 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument('-v', '--verbosity', help='increase output verbosity', 28 | action='store_true') 29 | args = parser.parse_args() 30 | print('libexiv2 version:', exiv2.version()) 31 | print('python-exiv2 version:', exiv2.__version__) 32 | print('python-exiv2 examples:', 33 | os.path.join(os.path.dirname(__file__), 'examples')) 34 | if args.verbosity: 35 | print('libexiv2 build options:') 36 | pprint.pprint(exiv2.versionInfo()) 37 | 38 | if __name__ == "__main__": 39 | sys.exit(main()) 40 | -------------------------------------------------------------------------------- /src/swig-0_27_7/basicio.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.types 9 | 10 | # Pull in all the attributes from the low-level C/C++ module 11 | if __package__ or "." in __name__: 12 | from ._basicio import * 13 | else: 14 | from _basicio import * 15 | 16 | 17 | import enum 18 | 19 | class PositionMeta(enum.EnumMeta): 20 | def __getattribute__(cls, name): 21 | obj = super().__getattribute__(name) 22 | if isinstance(obj, enum.Enum): 23 | import warnings 24 | warnings.warn( 25 | "Use 'BasicIo.Position' instead of 'Position'", 26 | DeprecationWarning) 27 | return obj 28 | 29 | class DeprecatedPosition(enum.IntEnum, metaclass=PositionMeta): 30 | pass 31 | 32 | Position = DeprecatedPosition('Position', _enum_list_Position()) 33 | Position.__doc__ = "Seek starting positions." 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/swig-0_27_7/datasets.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.metadatum 9 | import exiv2.value 10 | import exiv2.types 11 | 12 | # Pull in all the attributes from the low-level C/C++ module 13 | if __package__ or "." in __name__: 14 | from ._datasets import * 15 | else: 16 | from _datasets import * 17 | -------------------------------------------------------------------------------- /src/swig-0_27_7/easyaccess.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._easyaccess import * 11 | else: 12 | from _easyaccess import * 13 | -------------------------------------------------------------------------------- /src/swig-0_27_7/error.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._error import * 11 | else: 12 | from _error import * 13 | -------------------------------------------------------------------------------- /src/swig-0_27_7/exif.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.tags 9 | import exiv2.metadatum 10 | import exiv2.value 11 | import exiv2.types 12 | 13 | # Pull in all the attributes from the low-level C/C++ module 14 | if __package__ or "." in __name__: 15 | from ._exif import * 16 | else: 17 | from _exif import * 18 | -------------------------------------------------------------------------------- /src/swig-0_27_7/image.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.basicio 9 | import exiv2.types 10 | import exiv2.exif 11 | import exiv2.tags 12 | import exiv2.metadatum 13 | import exiv2.value 14 | import exiv2.iptc 15 | import exiv2.datasets 16 | import exiv2.xmp 17 | import exiv2.properties 18 | 19 | # Pull in all the attributes from the low-level C/C++ module 20 | if __package__ or "." in __name__: 21 | from ._image import * 22 | else: 23 | from _image import * 24 | -------------------------------------------------------------------------------- /src/swig-0_27_7/iptc.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.datasets 9 | import exiv2.metadatum 10 | import exiv2.value 11 | import exiv2.types 12 | 13 | # Pull in all the attributes from the low-level C/C++ module 14 | if __package__ or "." in __name__: 15 | from ._iptc import * 16 | else: 17 | from _iptc import * 18 | -------------------------------------------------------------------------------- /src/swig-0_27_7/metadatum.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.value 9 | import exiv2.types 10 | 11 | # Pull in all the attributes from the low-level C/C++ module 12 | if __package__ or "." in __name__: 13 | from ._metadatum import * 14 | else: 15 | from _metadatum import * 16 | -------------------------------------------------------------------------------- /src/swig-0_27_7/preview.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.image 9 | import exiv2.basicio 10 | import exiv2.types 11 | import exiv2.exif 12 | import exiv2.tags 13 | import exiv2.metadatum 14 | import exiv2.value 15 | import exiv2.iptc 16 | import exiv2.datasets 17 | import exiv2.xmp 18 | import exiv2.properties 19 | 20 | # Pull in all the attributes from the low-level C/C++ module 21 | if __package__ or "." in __name__: 22 | from ._preview import * 23 | else: 24 | from _preview import * 25 | -------------------------------------------------------------------------------- /src/swig-0_27_7/properties.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.metadatum 9 | import exiv2.value 10 | import exiv2.types 11 | 12 | # Pull in all the attributes from the low-level C/C++ module 13 | if __package__ or "." in __name__: 14 | from ._properties import * 15 | else: 16 | from _properties import * 17 | -------------------------------------------------------------------------------- /src/swig-0_27_7/tags.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.metadatum 9 | import exiv2.value 10 | import exiv2.types 11 | 12 | # Pull in all the attributes from the low-level C/C++ module 13 | if __package__ or "." in __name__: 14 | from ._tags import * 15 | else: 16 | from _tags import * 17 | -------------------------------------------------------------------------------- /src/swig-0_27_7/types.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._types import * 11 | else: 12 | from _types import * 13 | 14 | 15 | import os 16 | _dir = os.path.join(os.path.dirname(__file__), 'locale') 17 | if os.path.isdir(_dir): 18 | _set_locale_dir(_dir) 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/swig-0_27_7/value.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.types 9 | 10 | # Pull in all the attributes from the low-level C/C++ module 11 | if __package__ or "." in __name__: 12 | from ._value import * 13 | else: 14 | from _value import * 15 | 16 | 17 | import enum 18 | 19 | class CharsetIdMeta(enum.EnumMeta): 20 | def __getattribute__(cls, name): 21 | obj = super().__getattribute__(name) 22 | if isinstance(obj, enum.Enum): 23 | import warnings 24 | warnings.warn( 25 | "Use 'CommentValue.CharsetId' instead of 'CharsetId'", 26 | DeprecationWarning) 27 | return obj 28 | 29 | class DeprecatedCharsetId(enum.IntEnum, metaclass=CharsetIdMeta): 30 | pass 31 | 32 | CharsetId = DeprecatedCharsetId('CharsetId', _enum_list_CharsetId()) 33 | CharsetId.__doc__ = "Character set identifiers for the character sets defined by Exif." 34 | 35 | 36 | import enum 37 | 38 | class XmpArrayTypeMeta(enum.EnumMeta): 39 | def __getattribute__(cls, name): 40 | obj = super().__getattribute__(name) 41 | if isinstance(obj, enum.Enum): 42 | import warnings 43 | warnings.warn( 44 | "Use 'XmpValue.XmpArrayType' instead of 'XmpArrayType'", 45 | DeprecationWarning) 46 | return obj 47 | 48 | class DeprecatedXmpArrayType(enum.IntEnum, metaclass=XmpArrayTypeMeta): 49 | pass 50 | 51 | XmpArrayType = DeprecatedXmpArrayType('XmpArrayType', _enum_list_XmpArrayType()) 52 | XmpArrayType.__doc__ = "XMP array types." 53 | 54 | 55 | import enum 56 | 57 | class XmpStructMeta(enum.EnumMeta): 58 | def __getattribute__(cls, name): 59 | obj = super().__getattribute__(name) 60 | if isinstance(obj, enum.Enum): 61 | import warnings 62 | warnings.warn( 63 | "Use 'XmpValue.XmpStruct' instead of 'XmpStruct'", 64 | DeprecationWarning) 65 | return obj 66 | 67 | class DeprecatedXmpStruct(enum.IntEnum, metaclass=XmpStructMeta): 68 | pass 69 | 70 | XmpStruct = DeprecatedXmpStruct('XmpStruct', _enum_list_XmpStruct()) 71 | XmpStruct.__doc__ = "XMP structure indicator." 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/swig-0_27_7/version.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._version import * 11 | else: 12 | from _version import * 13 | -------------------------------------------------------------------------------- /src/swig-0_27_7/xmp.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.properties 9 | import exiv2.metadatum 10 | import exiv2.value 11 | import exiv2.types 12 | 13 | # Pull in all the attributes from the low-level C/C++ module 14 | if __package__ or "." in __name__: 15 | from ._xmp import * 16 | else: 17 | from _xmp import * 18 | -------------------------------------------------------------------------------- /src/swig-0_28_5/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | 5 | if sys.platform == 'win32': 6 | _dir = os.path.join(os.path.dirname(__file__), 'lib') 7 | if os.path.isdir(_dir): 8 | if hasattr(os, 'add_dll_directory'): 9 | os.add_dll_directory(_dir) 10 | os.environ['PATH'] = _dir + ';' + os.environ['PATH'] 11 | 12 | class Exiv2Error(Exception): 13 | """Python exception raised by exiv2 library errors. 14 | 15 | :ivar ErrorCode code: The Exiv2 error code that caused the exception. 16 | :ivar str message: The message associated with the exception. 17 | """ 18 | def __init__(self, code, message): 19 | self.code= code 20 | self.message = message 21 | 22 | #: python-exiv2 version as a string 23 | __version__ = "0.17.3" 24 | #: python-exiv2 version as a tuple of ints 25 | __version_tuple__ = tuple((0, 17, 3)) 26 | 27 | __all__ = ["Exiv2Error"] 28 | from exiv2.basicio import * 29 | __all__ += exiv2._basicio.__all__ 30 | from exiv2.datasets import * 31 | __all__ += exiv2._datasets.__all__ 32 | from exiv2.easyaccess import * 33 | __all__ += exiv2._easyaccess.__all__ 34 | from exiv2.error import * 35 | __all__ += exiv2._error.__all__ 36 | from exiv2.exif import * 37 | __all__ += exiv2._exif.__all__ 38 | from exiv2.image import * 39 | __all__ += exiv2._image.__all__ 40 | from exiv2.iptc import * 41 | __all__ += exiv2._iptc.__all__ 42 | from exiv2.metadatum import * 43 | __all__ += exiv2._metadatum.__all__ 44 | from exiv2.preview import * 45 | __all__ += exiv2._preview.__all__ 46 | from exiv2.properties import * 47 | __all__ += exiv2._properties.__all__ 48 | from exiv2.tags import * 49 | __all__ += exiv2._tags.__all__ 50 | from exiv2.types import * 51 | __all__ += exiv2._types.__all__ 52 | from exiv2.value import * 53 | __all__ += exiv2._value.__all__ 54 | from exiv2.version import * 55 | __all__ += exiv2._version.__all__ 56 | from exiv2.xmp import * 57 | __all__ += exiv2._xmp.__all__ 58 | 59 | __all__ = [x for x in __all__ if x[0] != '_'] 60 | __all__.sort() 61 | -------------------------------------------------------------------------------- /src/swig-0_28_5/__main__.py: -------------------------------------------------------------------------------- 1 | # python-exiv2 - Python interface to exiv2 2 | # http://github.com/jim-easterbrook/python-exiv2 3 | # Copyright (C) 2021-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import argparse 19 | import os 20 | import pprint 21 | import sys 22 | 23 | import exiv2 24 | 25 | def main(): 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument('-v', '--verbosity', help='increase output verbosity', 28 | action='store_true') 29 | args = parser.parse_args() 30 | print('libexiv2 version:', exiv2.version()) 31 | print('python-exiv2 version:', exiv2.__version__) 32 | print('python-exiv2 examples:', 33 | os.path.join(os.path.dirname(__file__), 'examples')) 34 | if args.verbosity: 35 | print('libexiv2 build options:') 36 | pprint.pprint(exiv2.versionInfo()) 37 | 38 | if __name__ == "__main__": 39 | sys.exit(main()) 40 | -------------------------------------------------------------------------------- /src/swig-0_28_5/basicio.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.types 9 | 10 | # Pull in all the attributes from the low-level C/C++ module 11 | if __package__ or "." in __name__: 12 | from ._basicio import * 13 | else: 14 | from _basicio import * 15 | 16 | 17 | import enum 18 | 19 | class PositionMeta(enum.EnumMeta): 20 | def __getattribute__(cls, name): 21 | obj = super().__getattribute__(name) 22 | if isinstance(obj, enum.Enum): 23 | import warnings 24 | warnings.warn( 25 | "Use 'BasicIo.Position' instead of 'Position'", 26 | DeprecationWarning) 27 | return obj 28 | 29 | class DeprecatedPosition(enum.IntEnum, metaclass=PositionMeta): 30 | pass 31 | 32 | Position = DeprecatedPosition('Position', _enum_list_Position()) 33 | Position.__doc__ = "Seek starting positions." 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/swig-0_28_5/datasets.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.metadatum 9 | import exiv2.value 10 | import exiv2.types 11 | 12 | # Pull in all the attributes from the low-level C/C++ module 13 | if __package__ or "." in __name__: 14 | from ._datasets import * 15 | else: 16 | from _datasets import * 17 | -------------------------------------------------------------------------------- /src/swig-0_28_5/easyaccess.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._easyaccess import * 11 | else: 12 | from _easyaccess import * 13 | -------------------------------------------------------------------------------- /src/swig-0_28_5/error.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._error import * 11 | else: 12 | from _error import * 13 | -------------------------------------------------------------------------------- /src/swig-0_28_5/exif.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.tags 9 | import exiv2.metadatum 10 | import exiv2.value 11 | import exiv2.types 12 | 13 | # Pull in all the attributes from the low-level C/C++ module 14 | if __package__ or "." in __name__: 15 | from ._exif import * 16 | else: 17 | from _exif import * 18 | -------------------------------------------------------------------------------- /src/swig-0_28_5/image.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.basicio 9 | import exiv2.types 10 | import exiv2.exif 11 | import exiv2.tags 12 | import exiv2.metadatum 13 | import exiv2.value 14 | import exiv2.iptc 15 | import exiv2.datasets 16 | import exiv2.xmp 17 | import exiv2.properties 18 | 19 | # Pull in all the attributes from the low-level C/C++ module 20 | if __package__ or "." in __name__: 21 | from ._image import * 22 | else: 23 | from _image import * 24 | -------------------------------------------------------------------------------- /src/swig-0_28_5/iptc.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.datasets 9 | import exiv2.metadatum 10 | import exiv2.value 11 | import exiv2.types 12 | 13 | # Pull in all the attributes from the low-level C/C++ module 14 | if __package__ or "." in __name__: 15 | from ._iptc import * 16 | else: 17 | from _iptc import * 18 | -------------------------------------------------------------------------------- /src/swig-0_28_5/metadatum.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.value 9 | import exiv2.types 10 | 11 | # Pull in all the attributes from the low-level C/C++ module 12 | if __package__ or "." in __name__: 13 | from ._metadatum import * 14 | else: 15 | from _metadatum import * 16 | -------------------------------------------------------------------------------- /src/swig-0_28_5/preview.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.image 9 | import exiv2.basicio 10 | import exiv2.types 11 | import exiv2.exif 12 | import exiv2.tags 13 | import exiv2.metadatum 14 | import exiv2.value 15 | import exiv2.iptc 16 | import exiv2.datasets 17 | import exiv2.xmp 18 | import exiv2.properties 19 | 20 | # Pull in all the attributes from the low-level C/C++ module 21 | if __package__ or "." in __name__: 22 | from ._preview import * 23 | else: 24 | from _preview import * 25 | -------------------------------------------------------------------------------- /src/swig-0_28_5/properties.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.metadatum 9 | import exiv2.value 10 | import exiv2.types 11 | 12 | # Pull in all the attributes from the low-level C/C++ module 13 | if __package__ or "." in __name__: 14 | from ._properties import * 15 | else: 16 | from _properties import * 17 | -------------------------------------------------------------------------------- /src/swig-0_28_5/tags.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.metadatum 9 | import exiv2.value 10 | import exiv2.types 11 | 12 | # Pull in all the attributes from the low-level C/C++ module 13 | if __package__ or "." in __name__: 14 | from ._tags import * 15 | else: 16 | from _tags import * 17 | -------------------------------------------------------------------------------- /src/swig-0_28_5/types.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._types import * 11 | else: 12 | from _types import * 13 | 14 | 15 | import os 16 | _dir = os.path.join(os.path.dirname(__file__), 'locale') 17 | if os.path.isdir(_dir): 18 | _set_locale_dir(_dir) 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/swig-0_28_5/value.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.types 9 | 10 | # Pull in all the attributes from the low-level C/C++ module 11 | if __package__ or "." in __name__: 12 | from ._value import * 13 | else: 14 | from _value import * 15 | 16 | 17 | import enum 18 | 19 | class CharsetIdMeta(enum.EnumMeta): 20 | def __getattribute__(cls, name): 21 | obj = super().__getattribute__(name) 22 | if isinstance(obj, enum.Enum): 23 | import warnings 24 | warnings.warn( 25 | "Use 'CommentValue.CharsetId' instead of 'CharsetId'", 26 | DeprecationWarning) 27 | return obj 28 | 29 | class DeprecatedCharsetId(enum.IntEnum, metaclass=CharsetIdMeta): 30 | pass 31 | 32 | CharsetId = DeprecatedCharsetId('CharsetId', _enum_list_CharsetId()) 33 | CharsetId.__doc__ = "Character set identifiers for the character sets defined by Exif." 34 | 35 | 36 | import enum 37 | 38 | class XmpArrayTypeMeta(enum.EnumMeta): 39 | def __getattribute__(cls, name): 40 | obj = super().__getattribute__(name) 41 | if isinstance(obj, enum.Enum): 42 | import warnings 43 | warnings.warn( 44 | "Use 'XmpValue.XmpArrayType' instead of 'XmpArrayType'", 45 | DeprecationWarning) 46 | return obj 47 | 48 | class DeprecatedXmpArrayType(enum.IntEnum, metaclass=XmpArrayTypeMeta): 49 | pass 50 | 51 | XmpArrayType = DeprecatedXmpArrayType('XmpArrayType', _enum_list_XmpArrayType()) 52 | XmpArrayType.__doc__ = "XMP array types." 53 | 54 | 55 | import enum 56 | 57 | class XmpStructMeta(enum.EnumMeta): 58 | def __getattribute__(cls, name): 59 | obj = super().__getattribute__(name) 60 | if isinstance(obj, enum.Enum): 61 | import warnings 62 | warnings.warn( 63 | "Use 'XmpValue.XmpStruct' instead of 'XmpStruct'", 64 | DeprecationWarning) 65 | return obj 66 | 67 | class DeprecatedXmpStruct(enum.IntEnum, metaclass=XmpStructMeta): 68 | pass 69 | 70 | XmpStruct = DeprecatedXmpStruct('XmpStruct', _enum_list_XmpStruct()) 71 | XmpStruct.__doc__ = "XMP structure indicator." 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/swig-0_28_5/version.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | # Pull in all the attributes from the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from ._version import * 11 | else: 12 | from _version import * 13 | -------------------------------------------------------------------------------- /src/swig-0_28_5/xmp.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (https://www.swig.org). 2 | # Version 4.3.0 3 | # 4 | # Do not make changes to this file unless you know what you are doing - modify 5 | # the SWIG interface file instead. 6 | 7 | from sys import version_info as _swig_python_version_info 8 | import exiv2.properties 9 | import exiv2.metadatum 10 | import exiv2.value 11 | import exiv2.types 12 | 13 | # Pull in all the attributes from the low-level C/C++ module 14 | if __package__ or "." in __name__: 15 | from ._xmp import * 16 | else: 17 | from _xmp import * 18 | -------------------------------------------------------------------------------- /tests/image_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jim-easterbrook/python-exiv2/9790df5991bb613a5b5d8985233120d36bdf969a/tests/image_01.jpg -------------------------------------------------------------------------------- /tests/image_02.heic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jim-easterbrook/python-exiv2/9790df5991bb613a5b5d8985233120d36bdf969a/tests/image_02.heic -------------------------------------------------------------------------------- /tests/image_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jim-easterbrook/python-exiv2/9790df5991bb613a5b5d8985233120d36bdf969a/tests/image_02.jpg -------------------------------------------------------------------------------- /tests/test_datasets.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import io 20 | import os 21 | import sys 22 | import tempfile 23 | import unittest 24 | 25 | import exiv2 26 | 27 | 28 | class TestDatasetsModule(unittest.TestCase): 29 | def check_result(self, result, expected_type, expected_value): 30 | self.assertIsInstance(result, expected_type) 31 | self.assertEqual(result, expected_value) 32 | 33 | def test_DataSet(self): 34 | dataset = exiv2.IptcDataSets.application2RecordList()[0] 35 | self.assertIsInstance(dataset, exiv2.DataSet) 36 | self.assertEqual(dataset['desc'][:27], 'A binary number identifying') 37 | self.check_result(dataset['mandatory'], bool, True) 38 | self.check_result(dataset['maxbytes'], int, 2) 39 | self.check_result(dataset['minbytes'], int, 2) 40 | self.check_result(dataset['name'], str, 'RecordVersion') 41 | self.check_result(dataset['number'], int, 0) 42 | self.check_result(dataset['photoshop'], str, '') 43 | self.check_result(dataset['recordId'], 44 | int, exiv2.IptcDataSets.application2) 45 | self.check_result(dataset['repeatable'], bool, False) 46 | self.check_result(dataset['title'], str, 'Record Version') 47 | self.check_result(dataset['type'], 48 | exiv2.TypeId, exiv2.TypeId.unsignedShort) 49 | 50 | def test_IptcDataSets(self): 51 | # number and record id of Iptc.Application2.Caption 52 | number = exiv2.IptcDataSets.Caption 53 | record_id = exiv2.IptcDataSets.application2 54 | # static data lists 55 | datasets = exiv2.IptcDataSets.application2RecordList() 56 | self.assertIsInstance(datasets, list) 57 | self.assertIsInstance(datasets[0], exiv2.DataSet) 58 | datasets = exiv2.IptcDataSets.envelopeRecordList() 59 | self.assertIsInstance(datasets, list) 60 | self.assertIsInstance(datasets[0], exiv2.DataSet) 61 | # test other methods 62 | self.check_result(exiv2.IptcDataSets.dataSet('Caption', record_id), 63 | int, exiv2.IptcDataSets.Caption) 64 | self.check_result(exiv2.IptcDataSets.dataSetDesc(number, record_id), 65 | str, 'A textual description of the object data.') 66 | self.check_result(exiv2.IptcDataSets.dataSetName(number, record_id), 67 | str, 'Caption') 68 | self.check_result(exiv2.IptcDataSets.dataSetPsName(number, record_id), 69 | str, 'Description') 70 | self.check_result(exiv2.IptcDataSets.dataSetRepeatable( 71 | number, record_id), bool, False) 72 | self.check_result(exiv2.IptcDataSets.dataSetTitle( 73 | number, record_id), str, 'Caption') 74 | self.check_result(exiv2.IptcDataSets.dataSetType( 75 | number, record_id), exiv2.TypeId, exiv2.TypeId.string) 76 | self.check_result(exiv2.IptcDataSets.recordDesc( 77 | record_id), str, 'IIM application record 2') 78 | self.check_result(exiv2.IptcDataSets.recordId('Application2'), 79 | int, exiv2.IptcDataSets.application2) 80 | self.check_result(exiv2.IptcDataSets.recordName( 81 | exiv2.IptcDataSets.application2), str, 'Application2') 82 | 83 | def test_IptcKey(self): 84 | key_name = 'Iptc.Application2.Caption' 85 | # constructors 86 | key = exiv2.IptcKey(key_name) 87 | self.assertIsInstance(key, exiv2.IptcKey) 88 | key2 = exiv2.IptcKey(key.tag(), key.record()) 89 | self.assertIsInstance(key2, exiv2.IptcKey) 90 | self.check_result(key2.key(), str, key_name) 91 | # copy 92 | key2 = key.clone() 93 | self.check_result(key2.key(), str, key_name) 94 | # other methods 95 | self.check_result(key.familyName(), str, key_name.split('.')[0]) 96 | self.check_result(key.groupName(), str, key_name.split('.')[1]) 97 | self.check_result(key.key(), str, key_name) 98 | self.check_result(key.record(), int, exiv2.IptcDataSets.application2) 99 | self.check_result(key.recordName(), str, key_name.split('.')[1]) 100 | self.check_result(key.tag(), int, exiv2.IptcDataSets.Caption) 101 | self.check_result(key.tagLabel(), str, key_name.split('.')[2]) 102 | self.check_result(key.tagName(), str, key_name.split('.')[2]) 103 | buf = io.StringIO() 104 | buf = key.write(buf) 105 | self.assertEqual(buf.getvalue(), key_name) 106 | 107 | 108 | if __name__ == '__main__': 109 | unittest.main() 110 | -------------------------------------------------------------------------------- /tests/test_easyaccess.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import os 20 | import unittest 21 | 22 | import exiv2 23 | 24 | 25 | class TestEasyaccessModule(unittest.TestCase): 26 | @classmethod 27 | def setUpClass(cls): 28 | test_dir = os.path.dirname(__file__) 29 | with open(os.path.join(test_dir, 'image_02.jpg'), 'rb') as f: 30 | image = exiv2.ImageFactory.open(f.read()) 31 | image.readMetadata() 32 | cls.exif_data = image.exifData() 33 | 34 | def check_result(self, datum, expected_type=None): 35 | # not all files have a value 36 | if datum is not None: 37 | self.assertIsInstance(datum, exiv2.Exifdatum) 38 | self.assertIsInstance(datum.getValue(), expected_type) 39 | 40 | def test_easyaccess(self): 41 | self.check_result(exiv2.afPoint(self.exif_data)) 42 | self.check_result(exiv2.contrast(self.exif_data)) 43 | self.check_result(exiv2.exposureMode(self.exif_data)) 44 | self.check_result(exiv2.exposureTime(self.exif_data)) 45 | self.check_result(exiv2.fNumber(self.exif_data), exiv2.URationalValue) 46 | self.check_result(exiv2.flashBias(self.exif_data)) 47 | self.check_result(exiv2.focalLength(self.exif_data), exiv2.URationalValue) 48 | self.check_result(exiv2.imageQuality(self.exif_data)) 49 | self.check_result(exiv2.isoSpeed(self.exif_data)) 50 | self.check_result(exiv2.lensName(self.exif_data), exiv2.AsciiValue) 51 | self.check_result(exiv2.macroMode(self.exif_data)) 52 | self.check_result(exiv2.make(self.exif_data)) 53 | self.check_result(exiv2.meteringMode(self.exif_data)) 54 | self.check_result(exiv2.model(self.exif_data)) 55 | self.check_result(exiv2.orientation(self.exif_data), exiv2.UShortValue) 56 | self.check_result(exiv2.saturation(self.exif_data)) 57 | self.check_result(exiv2.sceneCaptureType(self.exif_data)) 58 | self.check_result(exiv2.sceneMode(self.exif_data)) 59 | self.check_result(exiv2.serialNumber(self.exif_data)) 60 | self.check_result(exiv2.sharpness(self.exif_data)) 61 | self.check_result(exiv2.subjectDistance(self.exif_data)) 62 | self.check_result(exiv2.whiteBalance(self.exif_data)) 63 | if not exiv2.testVersion(0, 27, 4): 64 | self.skipTest('easyaccess funcs introduced in v0.27.4') 65 | self.check_result(exiv2.apertureValue(self.exif_data), exiv2.URationalValue) 66 | self.check_result(exiv2.brightnessValue(self.exif_data)) 67 | self.check_result(exiv2.dateTimeOriginal(self.exif_data), exiv2.AsciiValue) 68 | self.check_result(exiv2.exposureBiasValue(self.exif_data)) 69 | self.check_result(exiv2.exposureIndex(self.exif_data)) 70 | self.check_result(exiv2.flash(self.exif_data)) 71 | self.check_result(exiv2.flashEnergy(self.exif_data)) 72 | self.check_result(exiv2.lightSource(self.exif_data)) 73 | self.check_result(exiv2.maxApertureValue(self.exif_data)) 74 | self.check_result(exiv2.sensingMethod(self.exif_data)) 75 | self.check_result(exiv2.shutterSpeedValue(self.exif_data)) 76 | self.check_result(exiv2.subjectArea(self.exif_data)) 77 | 78 | 79 | if __name__ == '__main__': 80 | unittest.main() 81 | -------------------------------------------------------------------------------- /tests/test_error.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import enum 20 | import logging 21 | import os 22 | import unittest 23 | 24 | import exiv2 25 | 26 | 27 | class TestErrorModule(unittest.TestCase): 28 | @classmethod 29 | def setUpClass(cls): 30 | # clear locale 31 | name = 'en_US.UTF-8' 32 | os.environ['LC_ALL'] = name 33 | os.environ['LANG'] = name 34 | os.environ['LANGUAGE'] = name 35 | 36 | def check_result(self, result, expected_type, expected_value): 37 | self.assertIsInstance(result, expected_type) 38 | self.assertEqual(result, expected_value) 39 | 40 | def test_LogMsg(self): 41 | self.assertIsInstance(exiv2.LogMsg.Level, enum.EnumMeta) 42 | self.check_result( 43 | exiv2.LogMsg.level(), exiv2.LogMsg.Level, exiv2.LogMsg.Level.warn) 44 | exiv2.LogMsg.setLevel(exiv2.LogMsg.Level.debug) 45 | self.check_result( 46 | exiv2.LogMsg.level(), exiv2.LogMsg.Level, exiv2.LogMsg.Level.debug) 47 | # get exiv2 to log a message 48 | with self.assertLogs(level=logging.WARNING): 49 | comment = exiv2.CommentValue('charset=invalid Fred') 50 | # test setting and clearing handler 51 | self.assertEqual(exiv2.LogMsg.handler(), exiv2.LogMsg.pythonHandler) 52 | exiv2.LogMsg.setHandler(None) 53 | self.assertEqual(exiv2.LogMsg.handler(), None) 54 | exiv2.LogMsg.setHandler(exiv2.LogMsg.defaultHandler) 55 | self.assertEqual(exiv2.LogMsg.handler(), exiv2.LogMsg.defaultHandler) 56 | exiv2.LogMsg.setHandler(exiv2.LogMsg.pythonHandler) 57 | self.assertEqual(exiv2.LogMsg.handler(), exiv2.LogMsg.pythonHandler) 58 | # get exiv2 to raise an exception 59 | with self.assertRaises(exiv2.Exiv2Error) as cm: 60 | image = exiv2.ImageFactory.open(bytes()) 61 | self.assertEqual(cm.exception.code, 62 | exiv2.ErrorCode.kerInputDataReadFailed) 63 | 64 | 65 | if __name__ == '__main__': 66 | unittest.main() 67 | -------------------------------------------------------------------------------- /tests/test_metadatum.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import unittest 20 | 21 | import exiv2 22 | 23 | 24 | class TestMetadatumModule(unittest.TestCase): 25 | def test_Key(self): 26 | # C++ class is abstract 27 | pass 28 | 29 | def test_Metadatum(self): 30 | # C++ class is abstract 31 | pass 32 | 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/test_preview.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2022-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import os 20 | import sys 21 | import tempfile 22 | import unittest 23 | 24 | import exiv2 25 | 26 | 27 | class TestPreviewModule(unittest.TestCase): 28 | @classmethod 29 | def setUpClass(cls): 30 | test_dir = os.path.dirname(__file__) 31 | cls.image_path = os.path.join(test_dir, 'image_02.jpg') 32 | # read image file data into memory 33 | with open(cls.image_path, 'rb') as f: 34 | cls.image_data = f.read() 35 | cls.image = exiv2.ImageFactory.open(cls.image_data) 36 | cls.image.readMetadata() 37 | 38 | def check_result(self, result, expected_type, expected_value): 39 | self.assertIsInstance(result, expected_type) 40 | self.assertEqual(result, expected_value) 41 | 42 | def test_PreviewImage(self): 43 | manager = exiv2.PreviewManager(self.image) 44 | props = manager.getPreviewProperties() 45 | preview = manager.getPreviewImage(props[0]) 46 | self.assertIsInstance(preview, exiv2.PreviewImage) 47 | preview2 = exiv2.PreviewImage(preview) 48 | self.assertIsInstance(preview2, exiv2.PreviewImage) 49 | self.assertEqual(len(preview), preview.size()) 50 | copy = preview.copy() 51 | self.assertIsInstance(copy, exiv2.DataBuf) 52 | with preview.pData() as data: 53 | self.check_result(data, memoryview, copy) 54 | self.assertEqual(data[:10], b'\xff\xd8\xff\xe0\x00\x10JFIF') 55 | self.assertEqual(memoryview(preview), copy) 56 | self.check_result(preview.extension(), str, '.jpg') 57 | self.check_result(preview.height(), int, 120) 58 | self.check_result(preview.id(), int, 4) 59 | self.check_result(preview.mimeType(), str, 'image/jpeg') 60 | self.check_result(preview.size(), int, 2532) 61 | self.check_result(preview.width(), int, 160) 62 | if not exiv2.versionInfo()['EXV_ENABLE_FILESYSTEM']: 63 | self.skipTest('EXV_ENABLE_FILESYSTEM is off') 64 | with tempfile.TemporaryDirectory() as tmp_dir: 65 | temp_file = os.path.join(tmp_dir, 'image.jpg') 66 | self.assertEqual(preview.writeFile(temp_file), 2532) 67 | 68 | def test_PreviewManager(self): 69 | manager = exiv2.PreviewManager(self.image) 70 | self.assertIsInstance(manager, exiv2.PreviewManager) 71 | props = manager.getPreviewProperties() 72 | self.assertIsInstance(props, tuple) 73 | self.assertEqual(len(props), 1) 74 | prop = props[0] 75 | self.assertIsInstance(prop, exiv2.PreviewProperties) 76 | preview = manager.getPreviewImage(prop) 77 | self.assertIsInstance(preview, exiv2.PreviewImage) 78 | 79 | def test_PreviewProperties(self): 80 | properties = exiv2.PreviewManager(self.image).getPreviewProperties()[0] 81 | self.assertIsInstance(properties, exiv2.PreviewProperties) 82 | self.check_result(properties.extension_, str, '.jpg') 83 | self.check_result(properties.height_, int, 120) 84 | self.check_result(properties.id_, int, 4) 85 | self.check_result(properties.mimeType_, str, 'image/jpeg') 86 | self.check_result(properties.size_, int, 2532) 87 | self.check_result(properties.width_, int, 160) 88 | keys = properties.keys() 89 | self.assertIsInstance(keys, list) 90 | self.assertEqual(len(keys), 6) 91 | values = properties.values() 92 | self.assertIsInstance(values, list) 93 | self.assertEqual(len(values), 6) 94 | items = properties.items() 95 | self.assertIsInstance(items, list) 96 | self.assertEqual(len(items), 6) 97 | for k in properties: 98 | v = properties[k] 99 | self.assertIn(k, keys) 100 | self.assertIn(v, values) 101 | self.assertIn((k, v), items) 102 | with self.assertRaises(TypeError): 103 | properties[k] = 123 104 | with self.assertRaises(TypeError): 105 | del properties[k] 106 | with self.assertRaises(KeyError): 107 | a = properties['fred'] 108 | with self.assertRaises(KeyError): 109 | properties['fred'] = 123 110 | 111 | def test_ref_counts(self): 112 | # manager keeps reference to image 113 | self.assertEqual(sys.getrefcount(self.image), 2) 114 | manager = exiv2.PreviewManager(self.image) 115 | self.assertEqual(sys.getrefcount(self.image), 3) 116 | 117 | 118 | if __name__ == '__main__': 119 | unittest.main() 120 | -------------------------------------------------------------------------------- /tests/test_properties.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import io 20 | import os 21 | import sys 22 | import tempfile 23 | import unittest 24 | 25 | import exiv2 26 | 27 | 28 | class TestPropertiesModule(unittest.TestCase): 29 | key_name = 'Xmp.dc.description' 30 | namespace = 'http://purl.org/dc/elements/1.1/' 31 | prefix_name = 'dc' 32 | property_name = 'description' 33 | 34 | @classmethod 35 | def setUpClass(cls): 36 | exiv2.XmpParser.initialize() 37 | 38 | @classmethod 39 | def tearDownClass(cls): 40 | exiv2.XmpParser.terminate() 41 | 42 | def check_result(self, result, expected_type, expected_value): 43 | self.assertIsInstance(result, expected_type) 44 | self.assertEqual(result, expected_value) 45 | 46 | def test_XmpNsInfo(self): 47 | ns_info = exiv2.XmpProperties.nsInfo(self.prefix_name) 48 | self.assertIsInstance(ns_info, exiv2.XmpNsInfo) 49 | self.check_result(ns_info['desc'], str, 'Dublin Core schema') 50 | self.check_result(ns_info['ns'], str, self.namespace) 51 | self.check_result(ns_info['prefix'], str, self.prefix_name) 52 | property_info = ns_info['xmpPropertyInfo'] 53 | self.assertIsInstance(property_info, list) 54 | self.assertGreater(len(property_info), 0) 55 | self.assertIsInstance(property_info[0], exiv2.XmpPropertyInfo) 56 | 57 | def test_XmpProperties(self): 58 | properties = exiv2.XmpProperties 59 | self.check_result(properties.ns(self.prefix_name), str, self.namespace) 60 | self.check_result(properties.nsDesc(self.prefix_name), 61 | str, 'Dublin Core schema') 62 | self.assertIsInstance(properties.nsInfo(self.prefix_name), 63 | exiv2.XmpNsInfo) 64 | self.check_result(properties.prefix('http://purl.org/dc/elements/1.1/'), 65 | str, self.prefix_name) 66 | key = exiv2.XmpKey(self.key_name) 67 | key2 = exiv2.XmpKey('Xmp.dc.unknown') 68 | property_desc = properties.propertyDesc(key) 69 | self.assertIsInstance(property_desc, str) 70 | self.assertTrue(property_desc.startswith('A textual description of')) 71 | self.assertIsNone(properties.propertyDesc(key2)) 72 | property_info = properties.propertyInfo(key) 73 | self.assertIsInstance(property_info, exiv2.XmpPropertyInfo) 74 | self.assertIsNone(properties.propertyInfo(key2)) 75 | property_list = properties.propertyList(self.prefix_name) 76 | self.assertIsInstance(property_list, list) 77 | self.assertGreater(len(property_list), 0) 78 | self.assertIsInstance(property_list[0], exiv2.XmpPropertyInfo) 79 | self.check_result(properties.propertyTitle(key), str, 'Description') 80 | self.assertIsNone(properties.propertyTitle(key2)) 81 | self.check_result(properties.propertyType(key), 82 | exiv2.TypeId, exiv2.TypeId.langAlt) 83 | self.check_result(properties.propertyType(key2), 84 | exiv2.TypeId, exiv2.TypeId.xmpText) 85 | namespaces = properties.registeredNamespaces() 86 | self.assertIsInstance(namespaces, dict) 87 | self.assertGreater(len(namespaces), 0) 88 | self.assertEqual(namespaces[self.prefix_name], self.namespace) 89 | # these don't seem to have any effect 90 | properties.registerNs('http://example.com/', 'exmpl') 91 | properties.unregisterNs('http://example.com/') 92 | properties.unregisterNs() 93 | 94 | def test_XmpPropertyInfo(self): 95 | key = exiv2.XmpKey(self.key_name) 96 | property_info = exiv2.XmpProperties.propertyInfo(key) 97 | self.assertIsInstance(property_info, exiv2.XmpPropertyInfo) 98 | desc = property_info['desc'] 99 | self.assertIsInstance(desc, str) 100 | self.assertTrue(desc.startswith('A textual description of')) 101 | self.check_result(property_info['name'], str, 'description') 102 | self.check_result(property_info['title'], str, 'Description') 103 | self.check_result(property_info['typeId'], 104 | exiv2.TypeId, exiv2.TypeId.langAlt) 105 | self.check_result(property_info['xmpCategory'], 106 | exiv2.XmpCategory, exiv2.XmpCategory.External) 107 | self.check_result(property_info['xmpValueType'], str, 'Lang Alt') 108 | 109 | def test_XmpKey(self): 110 | # constructors 111 | key = exiv2.XmpKey(self.key_name) 112 | self.assertIsInstance(key, exiv2.XmpKey) 113 | key2 = exiv2.XmpKey(self.prefix_name, self.property_name) 114 | self.assertIsInstance(key2, exiv2.XmpKey) 115 | self.assertIsNot(key2, key) 116 | key2 = exiv2.XmpKey(key) 117 | self.assertIsInstance(key2, exiv2.XmpKey) 118 | self.assertIsNot(key2, key) 119 | # other methods 120 | self.assertEqual(str(key), self.key_name) 121 | key2 = key.clone() 122 | self.assertIsNot(key2, key) 123 | self.assertIsInstance(key2, exiv2.XmpKey) 124 | self.check_result(key.familyName(), str, self.key_name.split('.')[0]) 125 | self.check_result(key.groupName(), str, self.key_name.split('.')[1]) 126 | self.check_result(key.key(), str, self.key_name) 127 | self.check_result(key.ns(), str, self.namespace) 128 | self.check_result(key.tag(), int, 0) 129 | self.check_result(key.tagLabel(), str, 'Description') 130 | self.check_result(key.tagName(), str, self.key_name.split('.')[2]) 131 | buf = io.StringIO() 132 | buf = key.write(buf) 133 | self.assertEqual(buf.getvalue(), self.key_name) 134 | 135 | 136 | if __name__ == '__main__': 137 | unittest.main() 138 | -------------------------------------------------------------------------------- /tests/test_tags.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import io 20 | import os 21 | import unittest 22 | 23 | import exiv2 24 | 25 | 26 | class TestTagsModule(unittest.TestCase): 27 | key_name = 'Exif.Image.ImageDescription' 28 | group_name = 'Image' 29 | tag = 270 30 | 31 | @classmethod 32 | def setUpClass(cls): 33 | # clear locale 34 | name = 'en_US.UTF-8' 35 | os.environ['LC_ALL'] = name 36 | os.environ['LANG'] = name 37 | os.environ['LANGUAGE'] = name 38 | 39 | def check_result(self, result, expected_type, expected_value): 40 | self.assertIsInstance(result, expected_type) 41 | self.assertEqual(result, expected_value) 42 | 43 | def test_ExifTags(self): 44 | tags = exiv2.ExifTags 45 | key = exiv2.ExifKey(self.key_name) 46 | self.check_result(tags.defaultCount(key), int, 0) 47 | group_list = tags.groupList() 48 | self.assertIsInstance(group_list, list) 49 | self.assertGreater(len(group_list), 0) 50 | self.assertIsInstance(group_list[0], exiv2.GroupInfo) 51 | self.check_result(tags.ifdName(self.group_name), str, 'IFD0') 52 | self.check_result(tags.isExifGroup(self.group_name), bool, True) 53 | self.check_result(tags.isMakerGroup(self.group_name), bool, False) 54 | self.check_result(tags.sectionName(key), str, 'OtherTags') 55 | tag_list = tags.tagList(self.group_name) 56 | self.assertIsInstance(tag_list, list) 57 | self.assertGreater(len(tag_list), 0) 58 | self.assertIsInstance(tag_list[0], exiv2.TagInfo) 59 | 60 | def test_GroupInfo(self): 61 | info = exiv2.ExifTags.groupList()[0] 62 | self.check_result(info['groupName'], str, 'Image') 63 | if exiv2.testVersion(0, 28, 0): 64 | self.check_result(info['ifdId'], int, exiv2.IfdId.ifd0Id) 65 | else: 66 | self.check_result(info['ifdId'], int, 1) 67 | self.check_result(info['ifdName'], str, 'IFD0') 68 | tag_list = info['tagList']() 69 | self.assertIsInstance(tag_list, list) 70 | self.assertGreater(len(tag_list), 0) 71 | self.assertIsInstance(tag_list[0], exiv2.TagInfo) 72 | 73 | def test_TagInfo(self): 74 | info = exiv2.ExifTags.tagList(self.group_name)[0] 75 | self.check_result(info['count'], int, 0) 76 | desc = info['desc'] 77 | self.assertIsInstance(desc, str) 78 | self.assertTrue(desc.startswith('The name and version of the software')) 79 | if exiv2.testVersion(0, 28, 0): 80 | self.check_result(info['ifdId'], int, exiv2.IfdId.ifd0Id) 81 | else: 82 | self.check_result(info['ifdId'], int, 1) 83 | self.check_result(info['name'], str, 'ProcessingSoftware') 84 | if exiv2.testVersion(0, 28, 0): 85 | self.check_result( 86 | info['sectionId'], exiv2.SectionId, exiv2.SectionId.otherTags) 87 | else: 88 | self.check_result(info['sectionId'], int, 4) 89 | self.check_result(info['tag'], int, 11) 90 | self.check_result(info['title'], str, 'Processing Software') 91 | self.check_result( 92 | info['typeId'], exiv2.TypeId, exiv2.TypeId.asciiString) 93 | 94 | def test_ExifKey(self): 95 | # constructors 96 | key = exiv2.ExifKey(self.key_name) 97 | self.assertIsInstance(key, exiv2.ExifKey) 98 | key2 = exiv2.ExifKey(self.tag, self.group_name) 99 | self.assertIsInstance(key2, exiv2.ExifKey) 100 | self.assertIsNot(key2, key) 101 | key2 = exiv2.ExifKey(key) 102 | self.assertIsInstance(key2, exiv2.ExifKey) 103 | self.assertIsNot(key2, key) 104 | # other methods 105 | self.assertEqual(str(key), self.key_name) 106 | key2 = key.clone() 107 | self.assertIsNot(key2, key) 108 | self.assertIsInstance(key2, exiv2.ExifKey) 109 | self.check_result( 110 | key.defaultTypeId(), exiv2.TypeId, exiv2.TypeId.asciiString) 111 | self.check_result(key.familyName(), str, self.key_name.split('.')[0]) 112 | self.check_result(key.groupName(), str, self.key_name.split('.')[1]) 113 | self.check_result(key.idx(), int, 0) 114 | self.check_result(key.key(), str, self.key_name) 115 | key.setIdx(123) 116 | self.check_result(key.idx(), int, 123) 117 | self.check_result(key.tag(), int, self.tag) 118 | desc = key.tagDesc() 119 | self.assertIsInstance(desc, str) 120 | self.assertTrue(desc.startswith('A character string giving the title')) 121 | self.check_result(key.tagLabel(), str, 'Image Description') 122 | self.check_result(key.tagName(), str, self.key_name.split('.')[2]) 123 | buf = io.StringIO() 124 | buf = key.write(buf) 125 | self.assertEqual(buf.getvalue(), self.key_name) 126 | 127 | 128 | if __name__ == '__main__': 129 | unittest.main() 130 | -------------------------------------------------------------------------------- /tests/test_types.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023-24 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import locale 20 | import logging 21 | import os 22 | import random 23 | import sys 24 | import unittest 25 | 26 | import exiv2 27 | 28 | 29 | class TestTypesModule(unittest.TestCase): 30 | def check_result(self, result, expected_type, expected_value): 31 | self.assertIsInstance(result, expected_type) 32 | self.assertEqual(result, expected_value) 33 | 34 | def test_DataBuf(self): 35 | data = bytes(random.choices(range(256), k=128)) 36 | # constructors 37 | buf = exiv2.DataBuf() 38 | self.assertIsInstance(buf, exiv2.DataBuf) 39 | self.assertEqual(len(buf), 0) 40 | buf = exiv2.DataBuf(4) 41 | self.assertEqual(len(buf), 4) 42 | buf = exiv2.DataBuf(data) 43 | self.assertEqual(len(buf), len(data)) 44 | self.check_result(buf.data(), memoryview, data) 45 | # other methods 46 | self.check_result(buf.size(), int, len(data)) 47 | with buf.data() as view: 48 | self.assertIsInstance(view, memoryview) 49 | self.check_result(view[23], int, data[23]) 50 | view[49] = 99 51 | self.check_result(view[49], int, 99) 52 | buf = exiv2.DataBuf(data) 53 | self.assertEqual(buf, data) 54 | self.assertEqual(data, buf) 55 | self.assertNotEqual(buf, b'fred') 56 | self.assertNotEqual(b'fred', buf) 57 | if exiv2.testVersion(0, 28, 0): 58 | self.assertEqual(buf.cmpBytes(0, data), 0) 59 | self.assertEqual(buf.cmpBytes(5, data[5:]), 0) 60 | self.assertNotEqual(buf.cmpBytes(0, b'fred'), 0) 61 | buf.resize(6) 62 | self.assertEqual(len(buf), 6) 63 | self.assertEqual(buf.empty(), False) 64 | buf.resize(0) 65 | self.assertEqual(buf.empty(), True) 66 | else: 67 | with self.assertWarns(DeprecationWarning): 68 | result = buf[23] 69 | with self.assertWarns(DeprecationWarning): 70 | self.check_result(buf.pData_, memoryview, data) 71 | with self.assertWarns(DeprecationWarning): 72 | self.check_result(buf.size_, int, len(data)) 73 | buf.free() 74 | self.assertEqual(len(buf), 0) 75 | buf = exiv2.DataBuf(data) 76 | buf.reset() 77 | self.assertEqual(len(buf), 0) 78 | buf = exiv2.DataBuf() 79 | buf.alloc(6) 80 | self.assertEqual(len(buf), 6) 81 | 82 | def test_Rational(self): 83 | for type_ in (exiv2.Rational, exiv2.URational): 84 | # constructors 85 | value = type_() 86 | self.assertIsInstance(value, type_) 87 | value = type_(13, 7) 88 | self.assertIsInstance(value, type_) 89 | # other methods 90 | self.assertEqual(len(value), 2) 91 | self.assertEqual(repr(value), '(13, 7)') 92 | self.check_result(value[0], int, 13) 93 | self.check_result(value[1], int, 7) 94 | self.check_result(value.first, int, value[0]) 95 | self.check_result(value.second, int, value[1]) 96 | value[0] = 23 97 | self.check_result(value[0], int, 23) 98 | 99 | def test_TypeInfo(self): 100 | info = exiv2.TypeInfo 101 | self.check_result(info.typeId('Rational'), 102 | exiv2.TypeId, exiv2.TypeId.unsignedRational) 103 | self.check_result(info.typeName(exiv2.TypeId.unsignedRational), 104 | str, 'Rational') 105 | self.check_result(info.typeSize(exiv2.TypeId.unsignedRational), int, 8) 106 | 107 | @unittest.skipUnless(exiv2.versionInfo()['EXV_ENABLE_NLS'], 108 | 'no localisation available') 109 | def test_localisation(self): 110 | str_en = 'Failed to read input data' 111 | str_de = 'Die Eingabedaten konnten nicht gelesen werden.' 112 | # clear current locale 113 | locale.setlocale(locale.LC_ALL, 'C') 114 | self.check_result(exiv2.exvGettext(str_en), str, str_en) 115 | # set German locale 116 | if sys.platform == 'win32': 117 | name = 'German' 118 | else: 119 | name = 'de_DE.UTF-8' 120 | try: 121 | locale.setlocale(locale.LC_ALL, name) 122 | except locale.Error: 123 | self.skipTest("failed to set locale") 124 | return 125 | name = 'de_DE.UTF-8' 126 | os.environ['LC_ALL'] = name 127 | os.environ['LANG'] = name 128 | os.environ['LANGUAGE'] = name 129 | locale.setlocale(locale.LC_ALL, '') 130 | name, encoding = locale.getlocale() 131 | if name != 'de_DE' and sys.platform != 'win32': 132 | self.skipTest("locale environment ignored") 133 | # test localisation 134 | self.check_result(exiv2.exvGettext(str_en), str, str_de) 135 | if exiv2.testVersion(0, 28, 3) or not exiv2.testVersion(0, 28, 0): 136 | with self.assertLogs(level=logging.WARNING) as cm: 137 | comment = exiv2.CommentValue('charset=invalid Fred') 138 | self.assertEqual(cm.output, [ 139 | 'WARNING:exiv2:Ungültiger Zeichensatz: "invalid"']) 140 | with self.assertRaises(exiv2.Exiv2Error) as cm: 141 | key = exiv2.ExifKey('not.a.tag') 142 | self.assertEqual(cm.exception.message.replace('"', "'"), 143 | "Ungültiger Schlüssel 'not.a.tag'") 144 | # clear locale 145 | name = 'en_US.UTF-8' 146 | os.environ['LC_ALL'] = name 147 | os.environ['LANG'] = name 148 | os.environ['LANGUAGE'] = name 149 | 150 | 151 | if __name__ == '__main__': 152 | unittest.main() 153 | -------------------------------------------------------------------------------- /tests/test_version.py: -------------------------------------------------------------------------------- 1 | ## python-exiv2 - Python interface to libexiv2 2 | ## http://github.com/jim-easterbrook/python-exiv2 3 | ## Copyright (C) 2023 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | ## 5 | ## This program is free software: you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation, either version 3 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## This program is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with this program. If not, see 17 | ## . 18 | 19 | import unittest 20 | 21 | import exiv2 22 | 23 | 24 | class TestVersionModule(unittest.TestCase): 25 | def test_module(self): 26 | # earliest usable libexiv2 version 27 | result = exiv2.testVersion(0, 27, 0) 28 | self.assertIsInstance(result, bool) 29 | self.assertEqual(result, True) 30 | version = exiv2.version() 31 | self.assertIsInstance(version, str) 32 | self.assertGreaterEqual(version, '0.27.0') 33 | version_tuple = tuple(int(x) for x in version.split('.')[:3]) 34 | version = exiv2.versionNumber() 35 | self.assertIsInstance(version, int) 36 | self.assertEqual(version, (version_tuple[0] << 16) 37 | + (version_tuple[1] << 8) + version_tuple[2]) 38 | version = exiv2.versionNumberHexString() 39 | self.assertIsInstance(version, str) 40 | self.assertEqual( 41 | version, '{:02x}{:02x}{:02x}'.format(*version_tuple)) 42 | version = exiv2.versionString() 43 | self.assertIsInstance(version, str) 44 | self.assertGreaterEqual( 45 | version, '.'.join(str(x) for x in version_tuple)) 46 | 47 | 48 | if __name__ == '__main__': 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /utils/build_docs.py: -------------------------------------------------------------------------------- 1 | # python-exiv2 - Python interface to exiv2 2 | # http://github.com/jim-easterbrook/python-exiv2 3 | # Copyright (C) 2024 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import os 19 | import sys 20 | 21 | from sphinx.application import Sphinx 22 | 23 | 24 | def main(argv=None): 25 | root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 26 | src_dir = os.path.join(root, 'src', 'doc') 27 | dst_dir = os.path.join(root, 'doc', 'html') 28 | doctree_dir = os.path.join(root, 'doc', 'doctree') 29 | app = Sphinx(src_dir, src_dir, dst_dir, doctree_dir, 'html') 30 | app.build() 31 | return 0 32 | 33 | 34 | if __name__ == "__main__": 35 | sys.exit(main()) 36 | -------------------------------------------------------------------------------- /utils/tag_release.py: -------------------------------------------------------------------------------- 1 | # Photini - a simple photo metadata editor. 2 | # http://github.com/jim-easterbrook/Photini 3 | # Copyright (C) 2020 Jim Easterbrook jim@jim-easterbrook.me.uk 4 | # 5 | # This program is free software: you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see 17 | # . 18 | 19 | from datetime import date 20 | import re 21 | import sys 22 | 23 | # requires GitPython - 'sudo pip install gitpython' 24 | import git 25 | 26 | 27 | def main(argv=None): 28 | with open('README.rst') as rst: 29 | py_exiv2_version = rst.readline().split()[-1] 30 | message = 'python-exiv2-' + py_exiv2_version + '\n\n' 31 | with open('CHANGELOG.txt') as cl: 32 | while not cl.readline().startswith('Changes'): 33 | pass 34 | while True: 35 | line = cl.readline().strip() 36 | if not line: 37 | break 38 | message += line + '\n' 39 | repo = git.Repo() 40 | tag = repo.create_tag(py_exiv2_version, message=message) 41 | remote = repo.remotes.origin 42 | remote.push(tags=True) 43 | return 0 44 | 45 | 46 | if __name__ == "__main__": 47 | sys.exit(main()) 48 | --------------------------------------------------------------------------------