diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5580c29bc4..c68a85c200 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,10 +37,15 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libexpat1-dev zlib1g-dev libbrotli-dev # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} config-file: .github/codeql/codeql-config.yml @@ -52,7 +57,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -66,4 +71,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/on_PR_windows_matrix.yml b/.github/workflows/on_PR_windows_matrix.yml index 876ff38108..491c26071b 100644 --- a/.github/workflows/on_PR_windows_matrix.yml +++ b/.github/workflows/on_PR_windows_matrix.yml @@ -125,6 +125,7 @@ jobs: gtest:p libiconv:p zlib:p + brotli:p curl:p - name: Build @@ -175,6 +176,9 @@ jobs: libxslt-devel python38-lxml zlib-devel + libbrotlicommon1 + libbrotlidec1 + libbrotli-devel - name: Build run: | cmake -GNinja \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 147a392a4b..adb3ea3808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,13 +22,14 @@ include(cmake/mainSetup.cmake REQUIRED) option( BUILD_SHARED_LIBS "Build exiv2lib as a shared library" ON ) option( EXIV2_ENABLE_XMP "Build with XMP metadata support" ON ) option( EXIV2_ENABLE_EXTERNAL_XMP "Use external version of XMP" OFF ) -option( EXIV2_ENABLE_PNG "Build with png support (requires libz)" ON ) +option( EXIV2_ENABLE_PNG "Build with PNG support (requires zlib)" ON ) option( EXIV2_ENABLE_NLS "Build native language support (requires gettext)" OFF ) -option( EXIV2_ENABLE_LENSDATA "Build including lens data" ON ) +option( EXIV2_ENABLE_LENSDATA "Build including Nikon lens data" ON ) option( EXIV2_ENABLE_DYNAMIC_RUNTIME "Use dynamic runtime (used for static libs)" ON ) option( EXIV2_ENABLE_WEBREADY "Build webready support into library" OFF ) -option( EXIV2_ENABLE_CURL "USE Libcurl for HttpIo (WEBREADY)" OFF ) +option( EXIV2_ENABLE_CURL "Use libcurl for HttpIo (WEBREADY)" OFF ) option( EXIV2_ENABLE_BMFF "Build with BMFF support" ON ) +option( EXIV2_ENABLE_BROTLI "Use Brotli for JPEG XL compressed boxes (BMFF)" ON ) option( EXIV2_BUILD_SAMPLES "Build sample applications" OFF ) option( EXIV2_BUILD_EXIV2_COMMAND "Build exiv2 command-line executable" ON ) diff --git a/README.md b/README.md index 6aeb2f1837..2dff350b10 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,8 @@ Preset CMake variables: # CMake finds the project dependencies which were automatically handled by conan -- Conan: Using autogenerated FindZLIB.cmake --- Library zlib found C:/Users/luis/.conan/data/zlib/1.2.11/_/_/package/-- Conan: Using autogenerated FindCURL.cmake +-- Library zlib found C:/Users/luis/.conan/data/zlib/1.2.11/_/_/package/ +-- Conan: Using autogenerated FindCURL.cmake -- Library libcurl_imp found C:/Users/luis/.conan/data/libcurl/7.79.0/_/_/package/ ... @@ -245,12 +246,13 @@ Options defined at `exiv2/CMakeLists.txt` include: ```bash 576 rmills@rmillsmm:~/gnu/github/exiv2/exiv2 $ grep ^option CMakeLists.txt -option( BUILD_SHARED_LIBS "Build exiv2lib as a shared library" ON ) -option( EXIV2_ENABLE_XMP "Build with XMP metadata support" ON ) -option( EXIV2_ENABLE_EXTERNAL_XMP "Use external version of XMP" OFF ) -option( EXIV2_ENABLE_PNG "Build with png support (requires libz)" ON ) +option( BUILD_SHARED_LIBS "Build exiv2lib as a shared library" ON ) +option( EXIV2_ENABLE_XMP "Build with XMP metadata support" ON ) +option( EXIV2_ENABLE_EXTERNAL_XMP "Use external version of XMP" OFF ) +option( EXIV2_ENABLE_PNG "Build with png support (requires libz)" ON ) ... -option( EXIV2_ENABLE_BMFF "Build with BMFF support" ON ) +option( EXIV2_ENABLE_BMFF "Build with BMFF support (brotli recommended)" ON ) +option( EXIV2_ENABLE_BROTLI "Use Brotli for JPEG XL compressed boxes (BMFF)" ON ) 577 rmills@rmillsmm:~/gnu/github/exiv2/exiv2 $ ``` @@ -267,12 +269,13 @@ $ cmake -DBUILD_SHARED_LIBS=ON -DEXIV2_ENABLE_NLS=OFF The following Exiv2 features require external libraries: -| Feature | Package | Default | To change default | Availability | -|:-------------------------- |:-------- |:--------:| :---------------------------- |:----------- | -| PNG image support | zlib | ON | -DEXIV2\_ENABLE\_PNG=OFF | [http://zlib.net/](http://zlib.net/) | -| XMP support | expat | ON | -DEXIV2\_ENABLE\_XMP=OFF | [http://expat.sourceforge.net](http://expat.sourceforge.net)/
Use _**Expat 2.2.6**_ and later | -| Natural language system | gettext | OFF | -DEXIV2\_ENABLE\_NLS=ON | [http://www.gnu.org/software/gettext/](http://www.gnu.org/software/gettext/) | -| Character set conversion | libiconv | | Disabled for Visual Studio.
Linked when installed on UNIX like platforms. | [https://www.gnu.org/software/libiconv/](https://www.gnu.org/software/libiconv/) | +| Feature | Package | Default | To change default | Availability | +|:------------------------ |:-------- |:-------:|:--------------------------- |:------------ | +| PNG image support | zlib | ON | -DEXIV2\_ENABLE\_PNG=OFF | [http://zlib.net/](http://zlib.net/) | +| XMP support | expat | ON | -DEXIV2\_ENABLE\_XMP=OFF | [http://expat.sourceforge.net](http://expat.sourceforge.net)/
Use _**Expat 2.2.6**_ and later | +| Natural language system | gettext | OFF | -DEXIV2\_ENABLE\_NLS=ON | [http://www.gnu.org/software/gettext/](http://www.gnu.org/software/gettext/) | +| JPEG XL brob support | brotli | ON | -DEXIV2\_ENABLE\_BROTLI=OFF | [https://github.com/google/brotli](https://github.com/google/brotli) | +| Character set conversion | libiconv | | Disabled for Visual Studio.
Linked when installed on UNIX like platforms. | [https://www.gnu.org/software/libiconv/](https://www.gnu.org/software/libiconv/) | On UNIX systems, you may install the dependencies using the distribution's package management system. Install the development package of a dependency to install the header files and libraries required to build Exiv2. The script @@ -1214,7 +1217,7 @@ Update your system and install the build tools and dependencies (zlib, expat, gt ```bash $ sudo apt --yes update -$ sudo apt install --yes build-essential git clang ccache python3 libxml2-utils cmake python3 libexpat1-dev libz-dev zlib1g-dev libssh-dev libcurl4-openssl-dev libgtest-dev google-mock +$ sudo apt install --yes build-essential git clang ccache python3 libxml2-utils cmake python3 libexpat1-dev libz-dev zlib1g-dev libbrotli-dev libssh-dev libcurl4-openssl-dev libgtest-dev google-mock ``` For users of other platforms, the script /ci/install_dependencies.sh has code used to configure many platforms. The code in that file is a useful guide to configuring your platform. @@ -1247,23 +1250,23 @@ I recommend that you build and install CMake from source. ## MinGW/msys2 -Please note that the platform MinGW/msys2 32 is obsolete and superceded by MinGW/msys2 64. It is important to highlight that we rely on using the Universal C Runtime (UCRT) and its relatively new support for UTF-8. Check this [PR](https://github.com/Exiv2/exiv2/pull/2090) for more information. Therefore you will need to use the [URCT MSYS environment](https://www.msys2.org/docs/environments/). +Please note that the 32bit MinGW platform is obsolete and superceded by the 64bit MSYS2 distribution. It is important to highlight that we rely on using the Universal C Runtime (UCRT) and its relatively new support for UTF-8. Check this [PR](https://github.com/Exiv2/exiv2/pull/2090) for more information. Therefore you will need to use the [MSYS2 URCT64 environment](https://www.msys2.org/docs/environments/). Install the latest version of [MSYS2](https://repo.msys2.org/distrib/msys2-x86_64-latest.exe), and follow the installation instructions available [here](https://www.msys2.org/). -The CI workflow file `.github/workflows/on_PR_windows_matrix.yml` has a build job named `msys2` with instructions showing how to configure Exiv2 on MinGW/msys2. +The CI workflow file `.github/workflows/on_PR_windows_matrix.yml` has a build job named `msys2` with instructions showing how to configure Exiv2 on MSYS2. ### Install exiv2 Dependencies -Please note that you will need to install the `ucrt` package version of the exiv2 dependencies: +Please note that you will need to install the `ucrt-x86_64` package version of the exiv2 dependencies: ```bash -pacman -S mingw-w64-ucrt-x86_64-binutils mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-curl mingw-w64-ucrt-x86_64-expat mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-gettext mingw-w64-ucrt-x86_64-gtest mingw-w64-ucrt-x86_64-libiconv mingw-w64-ucrt-x86_64-make mingw-w64-ucrt-x86_64-zlib +pacman -S --needed mingw-w64-ucrt-x86_64-{brotli,cc,cmake,curl,expat,gettext,gtest,libiconv,libwinpthread,ninja,zlib} ``` ### Download exiv2 from github and build -Use the Windows start menu to open the terminal customized for the UCRT environment: `MSYS2 MinGW UCRT x64`. Then run the following commands to download exiv2, configure the project and build it: +Use the Windows start menu to open the terminal customized for the UCRT64 environment: `MSYS2 MinGW UCRT x64`. Then run the following commands to download exiv2, configure the project and build it: ```bash mkdir -p ~/gnu/github/exiv2 @@ -1271,7 +1274,7 @@ cd ~/gnu/github/exiv2 git clone https://github.com/exiv2/exiv2 cd exiv2 mkdir build && cd build -cmake -G "MSYS Makefiles" +cmake -G Ninja -DCMAKE_CXX_FLAGS=-Wno-deprecated -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON @@ -1282,10 +1285,10 @@ cmake -G "MSYS Makefiles" -DEXIV2_BUILD_UNIT_TESTS=ON .. -cmake --build . --parallel +cmake --build . ``` -The binaries generated at this point can be executed from the MSYS2 UCRT terminal, but they will not run from a Windows Command Prompt or PowerShell. The reason is that the MSYS2 UCRT terminal is properly configured to find some needed DLLs. In case you want to be able to run the generated **exiv2** binary from any Windows terminal, you'll need to deploy the needed DLLs with the application. +The binaries generated at this point can be executed from the MSYS2 UCRT64 terminal, but they will not run from a Windows Command Prompt or PowerShell. The reason is that the MSYS2 UCRT64 terminal is properly configured to find some needed DLLs. In case you want to be able to run the generated **exiv2** binary from any Windows terminal, you'll need to deploy the needed DLLs with the application. [TOC](#TOC)
diff --git a/ci/install_dependencies.sh b/ci/install_dependencies.sh index db890039f5..63885dbaa2 100755 --- a/ci/install_dependencies.sh +++ b/ci/install_dependencies.sh @@ -23,46 +23,46 @@ debian_build_gtest() { # workaround for really bare-bones Archlinux containers: if [ -x "$(command -v pacman)" ]; then pacman --noconfirm -Sy - pacman --noconfirm -S grep gawk sed + pacman --noconfirm --needed -S grep gawk sed fi distro_id=$(grep '^ID=' /etc/os-release|awk -F = '{print $2}'|sed 's/\"//g') case "$distro_id" in 'fedora') - dnf -y --refresh install gcc-c++ clang cmake make expat-devel zlib-devel libssh-devel libcurl-devel gtest-devel which dos2unix glibc-langpack-en diffutils + dnf -y --refresh install gcc-c++ clang cmake make expat-devel zlib-devel brotli-devel libssh-devel libcurl-devel gtest-devel which dos2unix glibc-langpack-en diffutils ;; 'debian') apt-get update - apt-get install -y cmake g++ clang make libexpat1-dev zlib1g-dev libssh-dev libcurl4-openssl-dev libgtest-dev libxml2-utils + apt-get install -y cmake g++ clang make libexpat1-dev zlib1g-dev libbrotli-dev libssh-dev libcurl4-openssl-dev libgtest-dev libxml2-utils debian_build_gtest ;; 'arch') pacman --noconfirm -Syu - pacman --noconfirm -S gcc clang cmake make expat zlib libssh curl gtest dos2unix which diffutils + pacman --noconfirm --needed -S gcc clang cmake make expat zlib brotli libssh curl gtest dos2unix which diffutils ;; 'ubuntu') apt-get update - apt-get install -y cmake g++ clang make libexpat1-dev zlib1g-dev libssh-dev libcurl4-openssl-dev libgtest-dev google-mock libxml2-utils + apt-get install -y cmake g++ clang make libexpat1-dev zlib1g-dev libbrotli-dev libssh-dev libcurl4-openssl-dev libgtest-dev google-mock libxml2-utils debian_build_gtest ;; 'alpine') apk update - apk add gcc g++ clang cmake make expat-dev zlib-dev libssh-dev curl-dev gtest gtest-dev gmock libintl gettext-dev which dos2unix bash libxml2-utils diffutils + apk add gcc g++ clang cmake make expat-dev zlib-dev brotli-dev libssh-dev curl-dev gtest gtest-dev gmock libintl gettext-dev which dos2unix bash libxml2-utils diffutils ;; 'centos'|'rhel') dnf clean all - dnf -y install gcc-c++ clang cmake make expat-devel zlib-devel libssh-devel libcurl-devel which dos2unix + dnf -y install gcc-c++ clang cmake make expat-devel zlib-devel brotli-devel libssh-devel libcurl-devel which dos2unix ;; 'opensuse-tumbleweed') zypper --non-interactive refresh - zypper --non-interactive install gcc-c++ clang cmake make libexpat-devel zlib-devel libssh-devel curl libcurl-devel git which dos2unix libxml2-tools + zypper --non-interactive install gcc-c++ clang cmake make libexpat-devel zlib-devel libbrotli-devel libssh-devel curl libcurl-devel git which dos2unix libxml2-tools pushd /tmp curl -LO https://github.com/google/googletest/archive/release-1.8.0.tar.gz tar xzf release-1.8.0.tar.gz diff --git a/cmake/FindBrotli.cmake b/cmake/FindBrotli.cmake new file mode 100644 index 0000000000..ea86ed79c6 --- /dev/null +++ b/cmake/FindBrotli.cmake @@ -0,0 +1,45 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +include(FindPackageHandleStandardArgs) + +find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") + +find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon) +find_library(BROTLIDEC_LIBRARY NAMES brotlidec) + +find_package_handle_standard_args(Brotli + FOUND_VAR + BROTLI_FOUND + REQUIRED_VARS + BROTLIDEC_LIBRARY + BROTLICOMMON_LIBRARY + BROTLI_INCLUDE_DIR + FAIL_MESSAGE + "Could NOT find Brotli" +) + +set(Brotli_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) +set(Brotli_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIDEC_LIBRARY}) + +mark_as_advanced(BROTLI_INCLUDE_DIR) +mark_as_advanced(BROTLICOMMON_LIBRARY) +mark_as_advanced(BROTLIDEC_LIBRARY) diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index c42d9ed88e..50f3961387 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -47,9 +47,12 @@ // Define if you have the header file. #cmakedefine EXV_HAVE_SYS_MMAN_H -// Define if you have are using the zlib library. +// Define if you have the zlib library. #cmakedefine EXV_HAVE_LIBZ +// Define if you have the brotli library. +#cmakedefine EXV_HAVE_BROTLI + /* Define if you have (Exiv2/xmpsdk) Adobe XMP Toolkit. */ #cmakedefine EXV_HAVE_XMP_TOOLKIT diff --git a/cmake/findDependencies.cmake b/cmake/findDependencies.cmake index 9315f43c06..2effaeefca 100644 --- a/cmake/findDependencies.cmake +++ b/cmake/findDependencies.cmake @@ -1,22 +1,20 @@ -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") - if (CONAN_AUTO_INSTALL) - # Download automatically the cmake-conan integration file - if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") - message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") - file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/master/conan.cmake" - "${CMAKE_BINARY_DIR}/conan.cmake" - TLS_VERIFY ON) - endif() + # Download automatically the cmake-conan integration file + if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") + message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") + file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/master/conan.cmake" + "${CMAKE_BINARY_DIR}/conan.cmake" + TLS_VERIFY ON) + endif() - include(${CMAKE_BINARY_DIR}/conan.cmake) + include(${CMAKE_BINARY_DIR}/conan.cmake) - conan_cmake_autodetect(settings) - conan_cmake_install(PATH_OR_REFERENCE .. - BUILD missing - REMOTE conancenter - OPTIONS webready=True - SETTINGS ${settings}) + conan_cmake_autodetect(settings) + conan_cmake_install(PATH_OR_REFERENCE .. + BUILD missing + REMOTE conancenter + OPTIONS webready=True + SETTINGS ${settings}) endif() if (APPLE) @@ -30,6 +28,8 @@ else() list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}) endif() +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") + find_package (Python3 COMPONENTS Interpreter) if (NOT Python3_Interpreter_FOUND) message(WARNING "Python3 was not found. Python tests under the 'tests' folder will not be executed") @@ -39,13 +39,17 @@ find_package(Filesystem REQUIRED) # don't use Frameworks on the Mac (#966) if (APPLE) - set(CMAKE_FIND_FRAMEWORK NEVER) + set(CMAKE_FIND_FRAMEWORK NEVER) endif() if( EXIV2_ENABLE_PNG ) find_package( ZLIB REQUIRED ) endif( ) +if( EXIV2_ENABLE_BMFF AND EXIV2_ENABLE_BROTLI ) + find_package( Brotli REQUIRED ) +endif( ) + if( EXIV2_ENABLE_WEBREADY ) if( EXIV2_ENABLE_CURL ) find_package(CURL REQUIRED) @@ -68,8 +72,8 @@ endif( ) find_package(Iconv) if( ICONV_FOUND ) - message ( "-- ICONV_INCLUDE_DIR : " ${Iconv_INCLUDE_DIR} ) - message ( "-- ICONV_LIBRARIES : " ${Iconv_LIBRARY} ) + message ( "-- Iconv_INCLUDE_DIRS : " ${Iconv_INCLUDE_DIRS} ) + message ( "-- Iconv_LIBRARIES : " ${Iconv_LIBRARIES} ) endif() if( BUILD_WITH_CCACHE ) @@ -80,4 +84,3 @@ if( BUILD_WITH_CCACHE ) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif() endif() - diff --git a/cmake/generateConfigFile.cmake b/cmake/generateConfigFile.cmake index 91b9554bfd..81081b5a76 100644 --- a/cmake/generateConfigFile.cmake +++ b/cmake/generateConfigFile.cmake @@ -20,6 +20,7 @@ else() endif() set(EXV_HAVE_ICONV ${ICONV_FOUND}) set(EXV_HAVE_LIBZ ${ZLIB_FOUND}) +set(EXV_HAVE_BROTLI ${BROTLI_FOUND}) check_cxx_symbol_exists(mmap sys/mman.h EXV_HAVE_MMAP ) check_cxx_symbol_exists(munmap sys/mman.h EXV_HAVE_MUNMAP ) diff --git a/cmake/printSummary.cmake b/cmake/printSummary.cmake index f1e6a033f0..2d8ea478f9 100644 --- a/cmake/printSummary.cmake +++ b/cmake/printSummary.cmake @@ -38,33 +38,34 @@ message( STATUS "RelWithDebInfo: ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}" ) message( STATUS "MinSizeRel: ${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}" ) message( STATUS "" ) message( STATUS "Compiler Options") -OptionOutput( "Warnings as errors: " EXIV2_TEAM_WARNINGS_AS_ERRORS ) -OptionOutput( "Use extra compiler warning flags: " EXIV2_TEAM_EXTRA_WARNINGS ) +OptionOutput( "Warnings as errors: " EXIV2_TEAM_WARNINGS_AS_ERRORS ) +OptionOutput( "Use extra compiler warning flags: " EXIV2_TEAM_EXTRA_WARNINGS ) message( STATUS "" ) -message( STATUS "------------------------------------------------------------------" ) -OptionOutput( "Building shared library: " BUILD_SHARED_LIBS ) -OptionOutput( "Building PNG support: " EXIV2_ENABLE_PNG AND ZLIB_FOUND ) +message( STATUS "------------------------------------------------------------------" ) +OptionOutput( "Building shared library: " BUILD_SHARED_LIBS ) +OptionOutput( "Building PNG support: " EXIV2_ENABLE_PNG AND ZLIB_FOUND ) if ( EXIV2_ENABLE_EXTERNAL_XMP ) - OptionOutput( "XMP metadata support (EXTERNAL): " EXIV2_ENABLE_EXTERNAL_XMP ) + OptionOutput( "XMP metadata support (EXTERNAL): " EXIV2_ENABLE_EXTERNAL_XMP ) else() - OptionOutput( "XMP metadata support: " EXIV2_ENABLE_XMP ) + OptionOutput( "XMP metadata support: " EXIV2_ENABLE_XMP ) endif() -OptionOutput( "Building BMFF support: " EXIV2_ENABLE_BMFF ) -OptionOutput( "Native language support: " EXIV2_ENABLE_NLS ) -OptionOutput( "Nikon lens database: " EXIV2_ENABLE_LENSDATA ) -OptionOutput( "Building webready support: " EXIV2_ENABLE_WEBREADY ) +OptionOutput( "Building BMFF support: " EXIV2_ENABLE_BMFF ) +OptionOutput( "Brotli support for JPEG XL: " EXIV2_ENABLE_BMFF AND BROTLI_FOUND ) +OptionOutput( "Native language support: " EXIV2_ENABLE_NLS ) +OptionOutput( "Nikon lens database: " EXIV2_ENABLE_LENSDATA ) +OptionOutput( "Building webready support: " EXIV2_ENABLE_WEBREADY ) if ( EXIV2_ENABLE_WEBREADY ) - OptionOutput( "USE Libcurl for HttpIo: " EXIV2_ENABLE_CURL ) + OptionOutput( "USE Libcurl for HttpIo: " EXIV2_ENABLE_CURL ) endif ( EXIV2_ENABLE_WEBREADY ) if (WIN32) - OptionOutput( "Dynamic runtime override: " EXIV2_ENABLE_DYNAMIC_RUNTIME) + OptionOutput( "Dynamic runtime override: " EXIV2_ENABLE_DYNAMIC_RUNTIME ) endif() -OptionOutput( "Building exiv2 command: " EXIV2_BUILD_EXIV2_COMMAND ) -OptionOutput( "Building samples: " EXIV2_BUILD_SAMPLES ) -OptionOutput( "Building unit tests: " EXIV2_BUILD_UNIT_TESTS ) -OptionOutput( "Building fuzz tests: " EXIV2_BUILD_FUZZ_TESTS ) -OptionOutput( "Building doc: " EXIV2_BUILD_DOC ) -OptionOutput( "Building with coverage flags: " BUILD_WITH_COVERAGE ) -OptionOutput( "Using ccache: " BUILD_WITH_CCACHE ) +OptionOutput( "Building exiv2 command: " EXIV2_BUILD_EXIV2_COMMAND ) +OptionOutput( "Building samples: " EXIV2_BUILD_SAMPLES ) +OptionOutput( "Building unit tests: " EXIV2_BUILD_UNIT_TESTS ) +OptionOutput( "Building fuzz tests: " EXIV2_BUILD_FUZZ_TESTS ) +OptionOutput( "Building doc: " EXIV2_BUILD_DOC ) +OptionOutput( "Building with coverage flags: " BUILD_WITH_COVERAGE ) +OptionOutput( "Using ccache: " BUILD_WITH_CCACHE ) diff --git a/conanfile.py b/conanfile.py index b9bed8185a..700028f3ba 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,23 +21,25 @@ def configure(self): self.options['gtest'].shared = False def requirements(self): - self.requires('zlib/1.2.12') + self.requires('zlib/1.2.13') + + self.requires('brotli/1.0.9') if self.options.webready: - self.requires('libcurl/7.80.0') + self.requires('libcurl/7.85.0') if os_info.is_windows and self.options.iconv: - self.requires('libiconv/1.16') + self.requires('libiconv/1.17') if self.options.unitTests: - self.requires('gtest/1.10.0') + self.requires('gtest/1.12.1') if self.settings.build_type == "Debug": self.options['gtest'].debug_postfix = '' if self.options.xmp: self.requires('XmpSdk/2016.7@piponazo/stable') # from conan-piponazo else: - self.requires('expat/2.4.8') + self.requires('expat/2.4.9') def imports(self): self.copy('*.dll', dst='bin', src='bin') diff --git a/include/exiv2/bmffimage.hpp b/include/exiv2/bmffimage.hpp index bde619cb0b..6956cfc65c 100644 --- a/include/exiv2/bmffimage.hpp +++ b/include/exiv2/bmffimage.hpp @@ -146,6 +146,13 @@ class EXIV2API BmffImage : public Image { static bool fullBox(uint32_t box); static std::string uuidName(const Exiv2::DataBuf& uuid); + /*! + @brief Wrapper around brotli to uncompress JXL brob content. + */ +#ifdef EXV_HAVE_BROTLI + static void brotliUncompress(const byte* compressedBuf, size_t compressedBufSize, DataBuf& arr); +#endif + }; // class BmffImage // ***************************************************************************** diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39039f4461..108f656012 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -232,6 +232,11 @@ if( EXIV2_ENABLE_PNG ) target_link_libraries( exiv2lib PRIVATE ZLIB::ZLIB) endif() +if( EXIV2_ENABLE_BMFF AND BROTLI_FOUND ) + target_link_libraries( exiv2lib PRIVATE ${Brotli_LIBRARIES}) + target_include_directories(exiv2lib PRIVATE ${Brotli_INCLUDE_DIRS}) +endif() + if( EXIV2_ENABLE_NLS ) target_link_libraries(exiv2lib PRIVATE ${Intl_LIBRARIES}) target_include_directories(exiv2lib PRIVATE ${Intl_INCLUDE_DIRS}) diff --git a/src/bmffimage.cpp b/src/bmffimage.cpp index a9b35c50de..9ff9dc1f98 100644 --- a/src/bmffimage.cpp +++ b/src/bmffimage.cpp @@ -15,6 +15,10 @@ #include "tiffimage_int.hpp" #include "types.hpp" +#ifdef EXV_HAVE_BROTLI +#include // for JXL brob +#endif + // + standard includes #include #include @@ -51,8 +55,9 @@ #define TAG_cmt3 0x434D5433 /**< "CMT3" canonID */ #define TAG_cmt4 0x434D5434 /**< "CMT4" gpsID */ #define TAG_colr 0x636f6c72 /**< "colr" Colour information */ -#define TAG_exif 0x45786966 /**< "Exif" Used by JXL*/ -#define TAG_xml 0x786d6c20 /**< "xml " Used by JXL*/ +#define TAG_exif 0x45786966 /**< "Exif" Used by JXL */ +#define TAG_xml 0x786d6c20 /**< "xml " Used by JXL */ +#define TAG_brob 0x62726f62 /**< "brob" Used by JXL (brotli box) */ #define TAG_thmb 0x54484d42 /**< "THMB" Canon thumbnail */ #define TAG_prvw 0x50525657 /**< "PRVW" Canon preview image */ @@ -158,6 +163,55 @@ std::string BmffImage::uuidName(const Exiv2::DataBuf& uuid) { return ""; } +#ifdef EXV_HAVE_BROTLI +void BmffImage::brotliUncompress(const byte* compressedBuf, size_t compressedBufSize, DataBuf& arr) { + BrotliDecoderState* decoder = NULL; + decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL); + if (!decoder) { + throw Error(ErrorCode::kerMallocFailed); + } + + size_t uncompressedLen = compressedBufSize * 2; // just a starting point + BrotliDecoderResult result; + int dos = 0; + size_t available_in = compressedBufSize; + const byte* next_in = compressedBuf; + size_t available_out; + byte* next_out; + size_t total_out = 0; + + do { + arr.alloc(uncompressedLen); + available_out = uncompressedLen - total_out; + next_out = arr.data() + total_out; + result = BrotliDecoderDecompressStream(decoder, &available_in, &next_in, &available_out, &next_out, &total_out); + if (result == BROTLI_DECODER_RESULT_SUCCESS) { + arr.resize(total_out); + } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + uncompressedLen *= 2; + // DoS protection - can't be bigger than 128k + if (uncompressedLen > 131072) { + if (++dos > 1) + break; + uncompressedLen = 131072; + } + } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { + // compressed input buffer in incomplete + throw Error(ErrorCode::kerFailedToReadImageData); + } else { + // something bad happened + throw Error(ErrorCode::kerErrorMessage, BrotliDecoderErrorString(BrotliDecoderGetErrorCode(decoder))); + } + } while (result != BROTLI_DECODER_RESULT_SUCCESS); + + BrotliDecoderDestroyInstance(decoder); + + if (result != BROTLI_DECODER_RESULT_SUCCESS) { + throw Error(ErrorCode::kerFailedToReadImageData); + } +} +#endif + uint64_t BmffImage::boxHandler(std::ostream& out /* = std::cout*/, Exiv2::PrintStructureOption option /* = kpsNone */, uint64_t pbox_end, size_t depth) { const size_t address = io_->tell(); @@ -452,6 +506,31 @@ uint64_t BmffImage::boxHandler(std::ostream& out /* = std::cout*/, Exiv2::PrintS case TAG_xml: parseXmp(buffer_size, io_->tell()); break; + case TAG_brob: { + enforce(data.size() >= 4, Exiv2::ErrorCode::kerCorruptedMetadata); + uint32_t realType = data.read_uint32(0, endian_); + if (bTrace) { + out << "type: " << toAscii(realType); + } +#ifdef EXV_HAVE_BROTLI + DataBuf arr; + brotliUncompress(data.c_data(4), data.size() - 4, arr); + if (realType == TAG_exif) { + uint32_t offset = Safe::add(arr.read_uint32(0, endian_), 4u); + enforce(Safe::add(offset, 4u) < arr.size(), Exiv2::ErrorCode::kerCorruptedMetadata); + Internal::TiffParserWorker::decode(exifData(), iptcData(), xmpData(), arr.c_data(offset), arr.size() - offset, + Internal::Tag::root, Internal::TiffMapping::findDecoder); + } else if (realType == TAG_xml) { + arr.resize(arr.size() + 1); + arr.write_uint8(arr.size() - 1, 0); // ensure xmp is null terminated! + try { + Exiv2::XmpParser::decode(xmpData(), std::string(arr.c_str())); + } catch (...) { + throw Error(ErrorCode::kerFailedToReadImageData); + } + } +#endif + } break; case TAG_thmb: switch (version) { case 0: // JPEG @@ -499,7 +578,7 @@ void BmffImage::parseTiff(uint32_t root_tag, uint64_t length, uint64_t start) { // hunt for "II" or "MM" const size_t eof = std::numeric_limits::max(); // impossible value for punt size_t punt = eof; - for (size_t i = 0; i < exif.size() - 8 && punt == eof; i += 2) { + for (size_t i = 0; i < exif.size() - 9 && punt == eof; ++i) { if (exif.read_uint8(i) == exif.read_uint8(i + 1)) if (exif.read_uint8(i) == 'I' || exif.read_uint8(i) == 'M') punt = i; diff --git a/src/version.cpp b/src/version.cpp index 04f5fcb786..3ae9cc8062 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -298,6 +298,7 @@ void Exiv2::dumpLibraryInfo(std::ostream& os, const std::vector& key int have_unistd_h = 0; int have_sys_mman = 0; int have_libz = 0; + int have_brotli = 0; int have_xmptoolkit = 0; int adobe_xmpsdk = 0; int have_bool = 0; @@ -380,6 +381,10 @@ void Exiv2::dumpLibraryInfo(std::ostream& os, const std::vector& key have_libz = 1; #endif +#ifdef EXV_HAVE_BROTLI + have_brotli = 1; +#endif + #ifdef EXV_HAVE_XMP_TOOLKIT have_xmptoolkit = 1; #endif @@ -472,6 +477,7 @@ void Exiv2::dumpLibraryInfo(std::ostream& os, const std::vector& key output(os, keys, "have_unistd_h", have_unistd_h); output(os, keys, "have_sys_mman", have_sys_mman); output(os, keys, "have_libz", have_libz); + output(os, keys, "have_brotli", have_brotli); output(os, keys, "have_xmptoolkit", have_xmptoolkit); output(os, keys, "adobe_xmpsdk", adobe_xmpsdk); output(os, keys, "have_bool", have_bool);